I don't believe in ORM object as your domain model

I have been on a few projects now where we have used ORM libraries to help store our data (eg ActiveRecord for Rails, Hibernate (and it’s variants), Castle’s ActiveRecord for .Net). On these projects, we have used the domain models and the ORM models are the same.

The problem we faced with doing so is the same problems I think most people face : eventually, your domain wants to take your model in one direction, and your relational database wants to take it in another. Personally, I really like Domain Driven Design – I like my objects to be reflective of the domain – and I don’t really care about how the database stores them, or indexes or stuff like that (I seem to remember something about BNF back in uni), so I am more inclined to let my model move towards the domain; but I do understand that there are consequences in doing so when looking at how my data is accessed.

I have now come to the belief that there is value in having my domain models but equally having ORM models. More so, I have decided that (despite the perceived increase in classes) there is a true distinction between the two and should be implemented as two separate classes. You can see from my examples what I am talking about, but basically my domain model knows nothing about the ORM model and anyone (apart from the necessary repository) knows about it either. The ORM model is used by the repository of concern as a helpful tool for it to store to the database; it could equally be done using SQL.

# The Domain model - yes there is more to it than that
class RegistrationForm

  def initialize(key)
    @key = key
    @name = ""
    @email = ""
  end
  ...
end
# The repository to find/save a registration
class RegistrationRepository

  def find(key)
    registration = find_registration(key)
    form = RegistrationForm.new
    return form unless registration
    update_form(form, registration)
  end

  def save(form)
    registration = find_registration(form[:key])
    registration = create_registration unless registration
    update_registration(registration, form)
    registration.save
  end

  private

    def find_registration(key)
      registration = Registration[:id => key]
     # This is the sequel equivalent of Registration.Find(key) in ActiveRecord
    end

    def create_registration
      registration = Registration.new
      registration
    end

    def update_form(form, registration)
      form.update(
        :key => registration.id,
        :name => registration.name,
        :email => registration.email)
      form
    end

    def update_registration(registration, form)
      registration.name = form[:name]
      registration.email = form[:email]
    end
end
# The ORM representation (using Sequel as my ORM library)
class Registration < Sequel::Model
  set_schema do
    primary_key :id
    varchar :name
    varchar :email
  end
end

Maybe this is something that other folk have already thought of, and I am just catching up, but the proliferation of web MVC style frameworks which mandate persistence layers (eg Rails) makes be think that this may need to be revisited.

This entry was posted in Design. Bookmark the permalink.

9 Responses to I don't believe in ORM object as your domain model

  1. David Ron says:

    Some people like to evolve their databases to meet their domain, but it sounds like you don’t like to refactor your database.
    http://en.wikipedia.org/wiki/Database_refactoring

    I think that you may have better luck using an object database than trying to slap a bunch of duct tape over relational database.

  2. Clinton Begin says:

    Hi Sarah,

    This is an interesting blog post for a couple of reasons. First, because I think this is a phase all developers go through… that of over abstraction. If you separate your domain model and your “ORM model”, and similarly you’ll probably have a form model for your web framework because you’ll eventually decide that you need a place to put lists of optional values or non-domain related form fields (e.g. “remember my username”). Then you’ll find yourself marshalling from the web to the form, loading domain objects, mapping them to orm objects, and finally persisting them to the database. Then one day someone will want an XML format of your domain and you’ll need to serialize it using XStream or something similar, but then where do you put your SOAP envelope? PHEW!!!

    Suddenly you’ll have the 7 layer cake design that cost so many applications their success. I saw Rails as the counter to this design, with its relatively flat design… the “smart object” design, where the domain is everything (from validations to web form fields, to mapped persistence fields etc).

    Anyway . The second reason I find your post interesting is that I agree with your statement that eventually, even if you begin with full control of your database, it ultimately diverges in its own direction. This usually happens once your application is successful and people start to notice that your database contains useful information. And along comes “Application B” — the other application or 3rd party tool that is built in some other language and expects completely different things of your database. All of a sudden the database is no longer your own.

    So you’re absolutely right to feel that disconnect early. However, the solution of using two layers of classes might be overkill (or just “kill” or perhaps “certain death” ;-) I fear you’ll regret that decision in the long run.

    A better solution is probably just plain old SQL and a good domain model, and a web framework that lets you wrap natural domain objects with forms (like Stripes). This keeps you to a single domain, a light web design and a flexible persistence solution. And about half as much code as you’re about to write. :-)

    I’ve always said, a project may start with ORM, but it will end with SQL.

    All the best,

    Clinton Begin
    Former ThoughtWorker

  3. David Moles says:

    So, I’ve only done about half a project in Rails, mostly just to see how hard it would be for me to do it, and (in the end) to prod the couple of old-school Perl hackers who originally volunteered for the project into getting off the couch and writing some code.

    But it seemed to me that the main advantage of Rails was that if I was willing to let the ORM model drive everything in both directions, Rails would happily take most persistence tasks and most of the “C” tasks in “MVC” (at least for basic use cases) off my plate. Unfortunately I was starting from a pre-existing database, so it wasn’t quite that easy, but that seemed to be the general idea.

    Since I come from a Java background, and I’ve been through everything from SQL-in-the-JSP to entity beans to home-grown SQL abstraction libraries to Hibernate and back, I totally understand where you’re coming from, about the model and the database evolving in different directions. But I guess what I’m wondering is, how much of what’s good about Rails do you keep, when you stop letting the ORM model drive?

  4. Toby Tripp says:

    Once I separate my ORM from my Model, the danger is I may now have to modify two classes whenever I want to add persistent data to my domain (three in your example!). I’m not okay with that.

    The responsibility of what data resides in which model does not belong to my persistence code. It belongs to my domain model, solely.

    I also don’t buy the idea that the database must eventually diverge from the domain model. The only way I see that happening is when other applications integrate at the database layer, positively the worst place to do that sort of integration.

    Work hard to keep your data model in line with your domain model. Resist the temptation to let others put their grubby hands down your data pants. Do that, and the motivation to keep DB Schema and Domain separate may fall away.

    Thanks for the provocative entry, keep it up.

    Toby

  5. Sai Venkat says:

    I agree with you. I wrote something similar a few days back actually. http://sai-venkat.tumblr.com/post/101030289/activerecord-domain-model.

    P.S. It is odd and freaky that many TW people think alike. This experience is happening to me for the third time :)

  6. Ben Butler-Cole says:

    I don’t know. I just don’t know. I can see that this is tempting. I’d love to be able to treat persistence as just another view on the model (a la Alistair Cockburn’s Hexagonal Architecture: http://alistair.cockburn.us/Hexagonal+architecture); and we often create view objects separate from the domain model. But isn’t it just a bit more complicated than that? Doesn’t the real world impinge?

    How does your example deal with object graphs? You don’t want to traverse the entire aggregate copying values just in case, do you? And can you do lazy loading?

    I’d love to know a real example of a problem that you are trying to solve with this solution that couldn’t be solved by fancy mapping in Hibernate or even a special-case repository method that drops down to raw SQL.

    You know what I really, really want? A framework that does all the fanch-shmancy stuff with object graph management and cleanliness tracking that a sophisticated ORM does, but just give me call-backs for dirty objects rather than persisting changes to a database. Then I really can have persistence as a view and ensure that all the views in my complex thick client get updated at the right time.

    Ben

  7. Sarah says:

    Thanks everyone for your comments, I will try to reply to your comments:

    At the moment, I am buying into the concept that your domain model shouldn’t know anything about other services – including how it is persisted. Exposing save, find etc operations on the models, as the ORMs do, breaks that concept.

    You may have extra pain mapping between your domain and ORM models but just how much, I’m not sure.

    @clinton – that sounds interesting…go straight for SQL. Then again, the ORM does make life easy to start with. Having said that, I find that often you usually need to tune your queries with ActiveRecord (rails) anyway.

    @david – yes, that is a powerful thing, iff you a building a database-table-on-the-screen application. For anything more, you tend to start cursing AR (and sometimes DHH’s attitude).

    @toby – the way I see databases diverging from the model is in how you store the data. I am mainly referring to the times when you need to tune your database for performance reasons (putting indexes, normalizing your tables etc).

    @sai – the butterfly effect maybe?

    (@ben – you know we basically had this on our last project).

  8. Dan Haywood says:

    Another way of creating an “ORM model” – isn’t it? – is to use good-ole fashioned views.

    Initially the view is 1:1 with the underlying table. As the two start to diverge, the view corresponds to the entity (the logical data model as we used to call it) and the table is, well, the physical data model.

    Of course, historically views have been for read-only access or very limited update access. But most RDBMS vendors (all the commercial ones) anyway, support INSTEAD OF triggers on views.

    To my mind this seems like a much more natural place to put the abstraction layer (as and when you need it). And it allows DBAs to easily do impact analysis when they want to change the physical database schema.

    Cheers
    Dan

  9. Torbjörn Gyllebring says:

    Great post! Conceptually I like to talk about and think about the following models:
    Persistance, Domain, and Presentation Model.
    Now for any given application we’re moving along a continium where at one end we have “The database on screen” basically how webapplciations been built for the last 15or so years and in many respects Rails seems to be the absolute peak of such frameworks, and it does work for many applications. On the other side we end up with all three models clearly visible in the design forced to crystalize because of the diffrent forces at work. The thins is that we always have theese, they just happen to align pretty perfectly with each other for a large class of applications. Experience teaches us to be mindfull of this and understand and know the source of our pains when we reach a point where conflicting needs start to pull them out of alignment and at that moment start pulling out new classes and concepts to ease that pain. Not knowing when todo so is just a big misstake as prematurly going for the 7layer cake mentioned earlier.
    As always, listen to the code and it will tell you how it wants to be.

Leave a Reply

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

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>