Thursday, August 20, 2009

10 orm patterns: components of a object-relational mapper

An Orm is a complex and generic tool which stores objects such as Entities and Value Objects (Customer, Groups, Money classes) in a relational database like MySQL or Sql Server, using metadata provided along with the Domain Model. The internals of an Orm usually follow some useful patterns that a developer should know to understand what is going on under the hood and here's a list of the most famous ones.
  • Table Data Gateway: an object that represent a table of the database, on a one-to-one basis. It is usually built as a generic class which can be subclasses or instantiated for any physical table.
  • Active Record: the most common approach, transforms a row of a table in an object. It strictly couples the object structures to the database tables by making the domain object subclassing an abstract implementation.
  • Data Mapper: a real Orm is an instance of a Data Mapper, a tool that stores objects which are ignorant of the environment where they will be kept, thus decoupling them from persistence concerns.
  • Unit Of Work: maintains a list of dirty objects and writes out the changeset. The purpose of this object is to keep track modified data on the entity object that it knows and its flushing capabilitities substitute the save() method of the Active Record implementations. It is a more resilient and sophisticated pattern than Active Record since it strives for persistence ignorance.
  • Repository: the persistent-ignorant equivalent of the Table Data Gateway. While a single repository implementation is aware of the database backend, a generic interface is placed between service classes and object retrieval mechanisms to aid decoupling.
  • Identity Map: a map of objects organized by class and primary key. It is a first level cache that contains every initialized object, so it can be used to prepare a changeset of database queries on flushing.
  • Lazy Loading: substituting a domain object or collection with a subclass that loads data on the fly when methods are requested, before forwarding the original call.
  • Query Object: a class that represents a query for retrieving objects, encapsulating Sql or other high level languages.
  • Criteria Object: a class that represents a set of criteria for selection of objects of a particular model.
  • Single Table Inheritance / Class Table Inheritance / Concrete Table Inheritance: patterns implemented to represent class inheritance in relational tables. Favoring composition over inheritance is a must because neither of these patterns does a perfect job in persisting data efficiently and in a clean way.
I hope you'll check out more information on the patterns you are using, maybe without even know that they exist. Feel free to add other useful patterns and approaches to the list in the comments.


Unknown said...

Hey Giorgio-

Interesting timing on your post today! We've just been discussing these issues as we are implementing rigorous testing of our Propel-based web app based on Misko's DI articles.

Fortunately Propel allows you to inject the dbCon everywhere. Unfortunately, it doesn't require it! Nor is the connection part of the Object instance so you have to remember to inject it everywhere. If you forget, you don't really know since it still works, but it may or may not be using your intended dbCon!

We have taken to using transactions to scope test runs and just roll them back after each test finishes. This is more performant than truncating the DB every time and easy to do as well. But sadly we learned the hard way how much dbCon injecting you need to do with Propel.

Also was reading Misko's post on ActiveRecord and it has some great points about the static accessors. Propel does that as well, but it feels wrong.

I am starting to lean towards looking for a better-architected ORM for ease of testing and development. Are there any that you prefer in the community today?


Anonymous said...

DB or Object, which is main?

please review Evans DDD.

Giorgio said...

@pub: DDD is a choice for very complex applications, sometimes even Active Record works well when the main features are CRUD.
@Alan: I'm not familiar with Propel but the standard replacement for it is Doctrine, for instance in Symfony. I worked on Doctrine 2 which is a real Data Mapper, based on a Unit Of Work: an alpha will be released in September. Currently there are not stable and reliable implementations of a Data Mapper in php, while for example Java has Hibernate and .Net has NHibernate.

Unknown said...

Ah I forgot you are working on Doctrine 2! I'll have to take a look at it when it's out of beta. I tried Doctrine 1 but it couldn't even rev-eng my database (I use postgis and ltree, both "custom" data types).

Unknown said...

Nice article, I'll check doctrine spec seems very interesting.