DDD: Life Cycle of a Domain Object

This post includes a lot of examples from the Ruby on Rails framework simply because I am working on it for my documentation project for this class. So I could easily see where these patterns where used in that framework. I am sure that other frameworks provide similar features and I am not advocating one over the other.

It's funny how sometimes when you read about something and that something pops up somewhere else. So, I was reading about the ActiveRecord (it's like the Active Record pattern in P or EAA but has more features so it might be a misnomer ) module in the Rails framework and they mentioned about using aggregation in the models. Here's the comment blurb directly from the source code:

rails-trunk/activerecord/lib/active_record/aggregations.rb:

Active Record implements aggregation through a macro-like class method called +composed_of+ for representing attributes as value objects. It expresses relationships like "Account [is] composed of Money [among other things]" or "Person [is] composed of [an] address". Each call to the macro adds a description of how the value objects [emphasis added] are created from the attributes of the entity object (when the entity is initialized either as a new object or from finding an existing object) and how it can be turned back into attributes (when the entity is saved to the database).

More information can be found on Rails aggregation here. Validation support is also built-in to the models.

I am sure that other frameworks out there support aggregations out-of-the-box as well but it is interesting to know that Rails does support some of the best practices in domain modeling. And how I would have used aggregations in Rails without even bothering with the fact that there was a pattern called Aggregations. Frameworks that make such patterns almost transparent are a great way to pick up patterns even without realizing it.

Aggregates are great for simplifying the issue of maintaining and handling transactions. Even if your database supported pessimistic or optimistic offline locking you would have to be very careful to order your database accesses to avoid deadlocks and. By using an aggregate properly, you can get away with some of that responsibility since all access to the database can only be done in larger chunks (the aggregate).

After discussing Aggregates, the chapter goes on to Factories. Factories is a generic term applied to the creational patterns such as the ones mentioned in Design Patterns: factory method, abstract factory and builder. Evans does not discuss each one in turn but shows how the concept of Factories are important to encapsulate the process of object creation so that that process of creation does not complicated the underlying domain. Some objects are hard to construct so expressing its construction process as a complicated region of code might distract the reader from the real domain underneath the system.

Finally the book presents Repositories. Repositories seem like a wrapper to me around the complex process of object retrieval. Just like object creation, object retrieval itself can be a complex process. For instance, if you are using a database, it is not uncommon to have to do joins across tables to actually get at the data that you want. However, executing a raw SQL query is not very clean; it exposes the encapsulation of what your tables are. Evans made sure to include the fact that sometimes, the framework already does a decent job of information retrieval for you. At least, that is the case with the Rails framework using reflection and meta-programming.

1  class Post < ActiveRecord::Base
2     has_one :author
3   end
4 
5   class Author < ActiveRecord::Base
6     belongs_to :post
7   end
Listing 1: Ruby code that expresses the relationship between classes
 1   CREATE TABLE posts (
 2     id int(11) NOT NULL auto_increment,
 3     title varchar default NULL,
 4     PRIMARY KEY  (id)
 5   )
 6 
 7   CREATE TABLE authors (
 8     id int(11) NOT NULL auto_increment,
 9     post_id int(11) default NULL,
10     name varchar default NULL,
11     PRIMARY KEY  (id)
12   )
Listing 2: The SQL table that represents the relation in Listing 1

With that you can do is:

1 # find_by_title is a method generated automatically based on your table schema
2 post = Post.find_by_title("First")
3 post.author # this will return the right author for that post 

It works for many-to-many relationships as well. More information can be found here.

What's interesting is that the three patterns discussed here all help to make sure that the underlying model of the system remains as uncluttered as possible. The implementation and the model should be bound together as advocated in Chapter 3, but it's also important that details of implementation - that might be the inherent limitation of the framework or programming language - do not interfere with the model. If there is too much interference, then no matter how you try to use a Layered Architecture, it will still be hard to extract the underlying logic for the model from the code.


comments powered by Disqus