Active Record vs. Data Mapper?

Published 30 April 07 08:40 AM | adrian

Suffice to say, Active Record is an approach towards natural grouping of behaviours between classes. Active Record is most commonly used to upgrade Data Trasfer Object towards Domain Model. On the other hand, Data Mapper downgrades Domain Model to DTO by taking away the data persistence responsibility.

When I first learned about layered programming, the most natural way to do separation is to use Data Source - Data Mapper - Business Layer - Presentation Layer path, with DTO all over the place. That is, Presentation uses DTO, Business Layer uses DTO, and so on... This creates a very unintuitive way to access data.

Say you have a Person object (heck, thousand books uses the Person object, I would, too), this Person is a DTO. That means it has private fields accompanied with getter/setter methods for each field. Say the Person has FirstName and LastName (again, just like thousand other books).

How does the Presentation (a.k.a. the End User) sees and uses this object?

Dim pers As Person = PersonBusiness.Get(personIdentity)
Write(pers.FirstName)
Write(pers.LastName)

While inside the PersonBusiness.Get method, the following code happens.

Return PersonMapper.Get(personIdentity)

And inside the PersonMapper.Get method, the following code happens.

Dim queryString As String = "SELECT * FROM Persons WHERE Id = " + personIdentity
Dim reader As IDataReader = db.ExecuteQuery(queryString)
If (reader.Read()) Then
  Dim pers As New Person()
  pers.FirstName = reader.ReadString(0)
  pers.LastName = reader.ReadString(1)
  Return pers
Else Return Nothing

To compare it with Active Record, here's how the Presentation sees and uses Active Record enabled DTO (or maybe, Domain Object).

Person pers = Person.Get(personIdentity);
Write(pers.FirstName);
Write(pers.LastName);

While inside the Person.Get method, the following code happens.

string queryString = "SELECT * FROM Persons WHERE Id = " + personIdentity;
IDataReader reader = db.ExecuteQuery(queryString);
if (reader.Read()) {
  Person pers = new Person();
  pers.FirstName = reader.ReadString(0);
  pers.LastName = reader.ReadString(1);
  return pers;
}
else { return null; }

Yep, Active Record is simpler because you eliminate the mapper layer, and the business layer. Fowler on Patterns of Enterprise Application Architecture said that you will need to use Data Mapper when things got more enterprise-y, or in other words, got more complex. But as you can see, it's completely unintuitive to use an external object to deal with another object's internals (that is, by requiring objects to expose unneeded information by public properties.

To illustrate this, say we take the Person object before. The only use of this object is to print his FullName using his FirstName and LastName. When using Data Mapper you'll need the current structure plus another public method that returns the concatenation of FirstName and LastName. You'll expose three methods for one function. If we used Active Record, you'll only need to expose the FullName method, the other two are not required (because you can address private fields using the internal mapper).

But then, can we combine these two patterns to create a better way of accessing things? Maybe like using Active Record that support independent evolution of Domain Model and data structure? Or maybe using Data Mapper without exposing unneeded properties?

Here comes Active Mapper(tm). :D

Active Mapper combines Active Record and Data Mapper capabilities using Dependency Injection to link between the two. Objects involved in this pattern are as follows:

  • Domain Model. This objects has data and behaviour that the Presentation layer can consume. It does not need to expose unneeded methods just to initialize the object.
  • Data Access Object. This object knows how to connect to the data source, queries it, and returns the resulting values.

So how does Active Mapper look like?

ConcreteDomain will retrieve data from data source using an implementation of IDataOperation<ConcreteDomain>. To achieve this, any implementation has to be "injected" into ConcreteDomain using Dependency Injection (be it at method-level for static methods or constructor-level for instance methods). ConcreteDataAccess implements IDataOperation<T>, returns values from data source. It is then up to the ConcreteDomain on how to map these values into its internal structure.

For example, client code will use ConcreteDomain (in this case the Person object) using the following code.

Person pers = Person.Get(personIdentity);
Write(pers.FullName);

While inside the Person.Get method, the following code happens.

object[] values = dataAccess.Read(personIdentity);
if (values != null) {
  Person pers = new Person();
  pers.firstName = (string)values[0]; // Private access
  pers.lastName = (string)values[1];  // is valid!
  return pers;
}
else { return null; }

As you can see, things evolve these ways:

  • Data source structure evolution happens on ConcreteDataAccess.
  • Domain structure (mapping) evolution happens on ConcreteDomain.
  • Data source operation evolution happens on IDataOperation<T>.

Didn't I mention that Generic Strategy is used?

While this looks even more complex than standard Data Mapper pattern, it does offer both Active Record and Data Mapper goodness.

Share this post: | | | |

Comments

No Comments