Tuesday, December 30, 2008

Moving from ActiveRecord to the Repository Pattern in my CMS

Some of you may know that I’ve been working on a CMS in my spare time.  This is partly so I can have something easy to publish my blog with, but mostly it’s just a learning exercise.  I’ve been working on and off on this thing for over a year.  Along the way, I’ve picked up some new C# skills, ASP.NET, some new SQL skills, learned VS Team Data, etc. Most recently, I’ve been digging into ASP.NET MVC, Test Driven Design, unit testing, dependency injection/inversion of control, and Domain Driven Design. 

So… I’ve decided to commit the ultimate sin and start the project over essentially from scratch, abandoning what I wrote before I learned all this new stuff in hopes that it’ll still save me time in the long run.

This post is a form of “thinking out loud” to help me work through my new design and convince myself it’s oatmeal (“the right thing to do”).  Comments are welcome.  Just don’t tell me I’m crazy; I already know that.

Initial Attempts, and Why I Abandoned Them

My first attempt at building the core CMS classes looked roughly like this (using my Blog model as an example):

BlogPostInfo – Read-only value object / data transfer object that basically represent a row in the database

IBlogDAL – Interface to abstract away my data access layer

BlogProvider – Implements IBlogDAL.  Makes calls to database for CRUD operations via ADO.NET.  Deals only with BlogPostInfo objects

DALFactory – factory class that knows how to instantiate the DAL implementations registered in web.config

BlogPost - “fat” class that has a hard-coded dependency on DALFactory. Adds some static methods for CRUD (passes through to the IBlogDAL implementation), adds setters for properties, adds lazy-loading “smart” properties for related objects.  Typical usage looks like:

BlogPost post = BlogPost.GetPostById(postId);



foreach(Comment comment in post.Comments)
{
   //...
}


The BlogPost.Comments property doesn’t exist on the thin read-only BlogPostInfo class (no “helper” methods do), only on the BlogPost class.  When the property is accessed, I call through to my Comments static class to a method that returns all comments for the current blog post ID.



This all made for an easy-to-use BLL, but I didn’t find it very easy to update or to unit test.  If I wanted to add or remove a method on the domain model, I may have to update as many as N different layers: the IXXXDAL interface, the DAL implementation, database artifacts (sprocs & tables, maybe also views), the XXXInfo read-only entity, and finally the BLL class in the “fat” object model.



Adding to the downsides of this approach, my BLL classes hard-coded their dependency on a DAL Factory class (a la the Pet Shop 4.0 example).  So each BLL class has a private “DAL” member variable that calls the factory, which in turn reads web.config to determine what class implements the requested data access type.  So, if I wanted to unit test my BlogPost objects without standing up a database, I’d have to replace web.config to point to a mock IBlogDAL.



So my goal in redesigning my core infrastructure was to reduce the number of layers to the bare minimum and to make my code easier to unit test.  I’m afraid this will come at the cost of usability of my core services components, but I’m willing to give it a try in the name of quality.



Domain Model



Taking a loose interpolation of a DDD approach, I’ll start by describing my new domain model.  Let’s look at the Blog model as an example:



I’m going with a thin read/write class for each of my domain entities.  For example:



public class BlogPost : BaseEntity
{
public string Title { get; set; }
public string Author { get; set; }
public string Markup { get; set; }
public DateTime PostedDate { get; set; }
}


BaseEntity is a simple base class that includes an ID parameter and requires derived classes to specify a “type” ID.  The type ID is an enumeration that includes things like BlogPosts, Articles, Photos, etc. I use it in tagging and commenting so I can implement generic tagging and commenting tables (tags and comments are linked to instances of objects by their ID and by their type ID in a SQL table.



I’ve decided against using interfaces to abstract away implementations of the domain model in order to keep things simple.  I’d love your opinions on this.  Personally, I don’t yet see the point of implementing an interface on a simple class that has no methods.



Repositories



I will use interfaces for each type of repository in order to make unit testing my code easier.  I’m going to keep repositories as simple as possible, ideally sticking to just CRUD operations for each object type.  For example:



public interface IBlogPostRepository
{
BlogPost GetBlogPostById(int id);
IList<BlogPost> GetAllBlogPosts();
bool Save(BlogPost post);
bool Delete(BlogPost post);
}


This feels pretty “raw” to me as far as something I’d consume from a controller (as opposed to the “fat” BLL approach above).  For example, my BlogPost BLL object had a method to approve posts (BlogPost.Approve()).  Using this approach, I’d have to do the following:



BlogPost post = blogRepository.GetBlogPostById(postId);
post.Approved = true;

post.Save();



In the old model, I would have done:



BlogPost post = BlogPost.GetBlogPostById(postId);
post.Approve();




Where do Services Fit In?



Here’s where I’m still waffling on the design. Should an IRepository include non-CRUD methods like encoding user-generated input from a forum post into HTML safe for rendering?  Should a repository implement validation functionality, such as making sure a BlogPost object has the author field specified before attempting to update it in the data store?



Essentially… where does the business logic fit in?  I’m thinking the answer is in services.



It seems to me that for some things, a repository should depend on business logic services such as validators.  In other cases, services should depend on repositories.  I’d love your thoughts on this.  Also, where do you draw the line and decide to implement a method as a service rather than on a repository?  For example, should ModerateComment be a method on an ICommentRepository or in an ICommentService?



And Another thing…



I really liked 1 thing about my ActiveRecord approach.  Classes in the BLL had behaviors, such as being commentable or taggable.  I had implemented these behaviors by specifying interfaces, ICommentable and ITaggable.  If a class inherited those interfaces, then a user could apply comments to tags to them.  It was nice because the behavior for the business object was encapsulated with the class. 



If I understand the repository/service approach correctly (and I most certainly do NOT), then I think I’d have to implement an ICommentService that knows how to apply comments to all domain entities.  I’m not comfortable with this – now the business objects (or what’s left of the concept of a business object) don’t know what they can or can’t do. 



For example, I’m thinking of ICommentService having the following methods:



public interface ICommentService
{
   AddCommentToObject(CommentInfo comment, BaseEntity target);
   ModerateComment(CommentInfo comment, bool approved);
   IsOkToPostComments(BaseEntity target, );
}


But what if I had some business objects that shouldn’t be commentable?  I suppose some of my options would be:



1. Derive a class CommentableEntity from BaseEntity and then derive any commentable classes from CommentableEntity.  If an object isn’t commentable, then it would just derive from BaseEntity instead of CommentableEntity.  C# doesn’t allow multiple inheritance though, so I couldn’t follow this approach for ITaggable or any other additional behaviors.



2. Include AllowComments and AllowTags properties on the BaseEntity class so al derived classes can decide whether or not they want to allow comments.



3. Stick with the ICommentable and ITaggable methods, but how do those fit in when your domain entities are super thin containers around properties only (at least that’s what all the examples I’ve seen indicate to do).



The Wrap Up



I’ll stop babbling for now – hopefully someone will read this and offer some sage wisdom to set me on the right track.  Thanks in advance!

2 comments:

Anonymous said...

I've been working through some of the same issues. Here's my opinion.

Your domain classes should still contain behavior. Don't put your business logic in services. Just because your moving away from the ActiveRecord pattern doesn't mean your model is should not be rich. That's the anemic domain model anti-pattern.

For example the way you had it before: BlogPost.Approve()
is good.

post.Approved = true;
not so much.

To me, the main difference using the repository pattern is you no longer have BlogPost.GetByPostId() and BlogPost.Save() etc., these are on the repository class instead, but keep the behaviors!

Joe Future said...

Thanks for the suggestions. I've been rewriting it all from scratch (will blog details soon), and I'm just getting back to the need for some business logic, so this is helpful. I'm still looking for a good example of when to use services, because this seems to make a lot of sense - embedding logic in the entity classes.