May 20, 2009

The domain object that does everything

Continuing with my open ended series of posts whereby I conduct a refactoring experiment with BlogEngine.NET

In my previous post ( Better readability using List.ForEach() and lambda expressions ) I refactored the newly created Catalog class and was left with some method calls that looked like this:

Post.PublishedPosts.ForEach(post => post.AddToCatalog());
 
The method for accessing the list of posts resides in the Post class itself, making the data access syntax somewhat similar to how it’s done with the ActiveRecord pattern or CSLA.

I don’t particularly like this structure, because it mixes what I consider to be two different concerns: domain (entity) logic and data persistence. It gives the Post class at least two distinct reasons to change, violating the Single Responsibility Principle and making the class probably a little more complex than it could be.

So, I decided to dive into the Post.cs file and see what I could do to simplify the class and remove some of the concerns that don’t fit directly in the domain object. What exactly is in the Post class? What does it do? After a quick once-through, I could see that the Post class:

  • Has properties that define the “Post” entity, like Title, Author, Content, etc.
  • Has event handlers that fire when posts are added, removed, rated, etc.
  • Manages an in-memory list of “Post” entities
  • Coordinates persistence to the data store by using the BlogService class
  • Formats its own relative, absolute, trackback, and permalink URIs by accessing the Utils and BlogSettings classes
  • Sends its own emails by calling out to the Utils class

There’s a lot going on in there. Personally, I think the domain object / entity class only needs to be concerned with the first two items in the list, and it should delegate out the rest of the stuff.

For the persistence, what I would prefer to see is something more like the Repository pattern used in Domain Driven Design. With a repository, all of the persistence is routed through a single class that acts “like an in-memory domain object collection.” Which, coincidentally, is exactly what’s stuffed inside the Post class. I just need to let it out.

In our case, the Post repository would be a class called Posts, physically and logically separated from the domain object’s Post class. We would end up with something that looks like this:

Posts.Published.ForEach(post => post.AddToCatalog());
 
From a syntax perspective, there’s only a subtle difference, but it’s an important one. We’re now treating the collection of Posts as a first-class object. And, by separating the persistence concerns out of the Post.cs class, it becomes much simpler and easier to maintain. As does the repository (Posts.cs) itself.

Persistence, however, isn’t the only external concern that needs to be pulled out. The URI generation is now in a class called RoutingAgent, which is also where I placed former Utils methods that calculate the RelativeWebRoot and AbsoluteWebRoot and such. The email logic is in the MailAgent class, as an extension method:

public static void SendNotifications(this Comment comment, Post post)
 
In the end, the Post.cs class is greatly simplified, and the post entity is no longer tightly coupled with non-post concerns like System.Net.Mail, BlogEngine.Core.Providers, System.Globalization, etc.

Next I’ll have to do the same type of refactoring with the other domain objects (Page, Comment, etc.).