Projects using Ruby on Rails often lack strong distinctions in two main areas:

  • The model/record conflation: Seeing “models” as strictly DB-backed resources.
  • The view/template conflation: failing to draw a line between view objects and HTML templates.

The conflations are encouraged by Rails’ design decisions. When choosing which enterprise design patterns to translate into code, the Rails developers chose Active Record (a relatively thin, one-to-one take on the ORM idea), and Template View.

I am not faulting them for this decision. These were both thoroughly pragmatic decisions which mapped well to the kind of applications that Rails was aimed at. By encoding these opinions into the framework and covering them with a sweet coating of convention-over-configuration, Rails saved thousands of projects from pattern analysis paralysis.

But now Rails applications are maturing, and a lot of developers are starting to realize that these patterns may not scale so well to more and more complex applications.  When figuring out how to move forward, we could do much worse than to consult the same book that laid the blueprints for Rails’ architecture, Patterns of Enterprise Application Architecture.

Here’s Martin Fowler on Domain Models:

An OO domain model will often look similar to a database model, yet it will still have a lot of differences. A Domain Model mingles data and process, has multivalued attributes and a complex web of associations, and uses inheritance.

As a result I see two styles of Domain Model in the field. A simple Domain Model looks very much like the database design with mostly one domain object for each database table. A rich Domain Model can look different from the database design, with inheritance, strategies, and other [Gang of Four] patterns, and complex webs of interconnected objects. A rich Domain Model is better for more complex logic, but is harder to map to the database. A simple Domain Model can use Active Record, whereas a rich Domain Model requires Data Mapper.

Note that Fowler is referring to the Active Record and Data Mapper design patterns, not to the Ruby libraries those patterns inspired. There is a correspondence, though: the DataMapper library, like the pattern it is based on, puts a higher value on decoupling the domain model from the database schema.

Now on to views. Here’s Fowler talking about Two Step Views, an alternative to the Template View pattern:

You may… want to want to make global changes to the appearance of the site easily, but common approaches using Template View or Transform View make this difficult because presentation decisions are often duplicated across multiple pages or transform modules. A global change can force you to change several files.

Two Step View deals with this problem by splitting the transformation into two stages. The first transforms the model data into a logical presentation without any special formatting; the second converts that logical presentation with the actual formatting needed.

I do a lot of two-step view processing in my projects. I haven’t had a chance to take a close look at, but I think this process is also what Jeff Casimir is aiming at with Draper, and I support and applaud his efforts.

And that’s all I had to say about that… I just wanted to post those quotes in case anyone else found them noteworthy.

Enhanced by Zemanta

Published by Avdi Grimm

18 Comments

  1. Thanks for these. Both of these issues are things that I’ve come to realize over the last months or two, and it looks like using DM and/or Draper is going to really help my Rails code.

    Reply
    • I have a longish post living in my head that will take people through building a model as a domain object first and then adding AR as a persistence implementation detail.  Dunno when I’ll have the slack to type it up.

      Reply
      • I want to do exactly that in one of my current projects. I’ve been thinking about using plain old ruby objects extended with Virtus so I can use its coercion system and custom attribute classes and then have a bunch of AR models that will be used only to perform CRUD operations on the db. 

        Reply
      • That’s exactly what I need to read.  I would love to see how you approach that.  A project I’m working on may need to move in that direction, and seeing your take would help a lot.

        Reply
      • just wanted to toss a vote in – please do write this up! I’d like you to elaborate on some of the material covered by Dan Chak in Enterprise Rails as it pertains to your experiences and opinions

        Reply
  2. Decoupling the domain model from the data model was evangelized by Dan Chak a few years ago in his book “Enterprise Rails” and subsequent presentations.

    The challenge is deciding when to use the enterprisey patterns vs out-of-the-box Rails defaults. There’s clearly some overhead which is probably waste for early/small projects, but there are great benefits for mature/large projects. I’d guess most projects don’t live long enough or grow large enough to make the overhead worthwhile, and it’s not easy to refactor in that direction.

    Reply
    • The ideal, I think, would be libraries which make the transition a little easier. I’ve been giving this a lot of thought lately. For instance, it’s completely possible to write a view layer that lets you just write templates the way you do in Rails–but in the background it constructs a data-transformation object model using conventions which you can then hook into and customize later on. Someone just needs to find the time to do it…

      Reply
      • Yes. There also seems to be growing momentum towards moving more of the view to JavaScript/CoffeeScript, so maybe some better abstractions will shake out in that transition.

        Also, acceptance/integration testing options have gotten better the past couple years in Capybara and Cucumber, which should make refactoring a little easier.

        Reply
  3. Your comments are specific to Rails, but the same comments could be made about Grails (s/ActiveRecord/GORM) and several other “higher-order” web frameworks. As usage of these frameworks matures beyond relatively simple database manipulation applications, better abstraction between the db and view layer will become more important.

    Reply
  4. Great post, Avdi. I’ve been thiunking a lot about the high number of PoEAA patterns present in Rails and more recently throughout the Ruby ecosystem. A lot of re-learning going on, so putting a spotlight on content like the PoEAA book will certainly help some developers avoid repeating mistakes of the past.

    -Bryan

    Reply
  5. Excellent post, thanks! I’m relatively new to Rails, and recently I’ve been starting to wonder how to push my Rails projects more towards Hexagonal Architecture thinking (see http://silkandspinach.net/2005/03/22/the-middle-hexagon-should-be-independent-of-the-adapters/ for example). I’m experimenting with “Page” or “View” objects — simple DTOs that break the links between views, controllers and AR objects. Nice to know others are doing the same 🙂

    Reply
  6. you should probably take a look at Domain Driven Design, Eric Evans. specifically the Repository pattern, the recommendation is to read the last section (strategic design) first…

    Reply
  7. Avdi, very good post! I have been thinking about the same problems lately. But it seems there is one typo in your post:

    The datamapper gem ( http://datamapper.org ) doesn’t look like an implementation of the datamapper pattern ( http://martinfowler.com/eaaCatalog/dataMapper.html ). For the datamapper pattern, an intermediate mapper class encapsulates the query logics. To quote the example on the book, I should see something like

    person = person_mapper.find_by_name(‘my_name’)

    But the datamapper gem is mixing in the query logics into the model, so it becomes similar to ActiveRecord:

    person = Person.find_by_name(‘my_name’)

    Technically, the datamapper gem is still an implementation of the ActiveRecord pattern ( http://martinfowler.com/eaaCatalog/activeRecord.html ).

    Reply
    • DataMapper project is indeed a more “Active Record”-ish implementation; however, it does have elements of Data Mapper pattern.

      There’s already a work-in-progress towards DataMapper 2.0 which will be a true implementation of the Data Mapper pattern as described in PoEAA. We’re putting together a roadmap describing general architecture: https://github.com/datamapper/dm-core/wiki/Roadmap

      Reply
  8. The Datamapper website is full of code examples that put column mappings straight in the model classes, and that uses the model classes to create objects directly in a database. As such, from a user perspective, at least if you follow the examples at datamapper.org, it’s not really related to Fowler’s datamapper pattern at all.

    I’ve yet to actually see an example of code written using DataMapper that implements the data mapper pattern…

    Reply
  9. Avdi, you know what? I’m going to read all your posts in the near future. Every time I have some spare time to try digging into something that interests me I finally come across your site. It’s a sort of sign you know 😉

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *