Well from the book POEAA, Martin Fowler introduced this idea of Unit of Work. It works very well if you want to have auto-commit system, in which your domain model uses Unit of work to label itself as new, dirty, removed or clean. Then you only need to call UnitofWork.commit() and all changes of models will be saved. Below is a domain model class with such methods:
public abstract class DomainModel{
protected void markNew(){
UnitOfWork.getCurrent().registerNew(this);
}
protected void markDirty(){
UnitOfWork.getCurrent().registerDirty(this);
}
protected void markRemoved(){
UnitOfWork.getCurrent().registerRemoved(this);
}
protected void markClean(){
UnitOfWork.getCurrent().registerClean(this);
}
}
With this implementation, you can mark a domain model as any save state through business logic method:
public class Message extends DomainModel{
public void updateContent(User user, string content){
// This method update message content if the the message posted time is not longer than 24 hrs, and the user has permission to update messate content.
if(!canUpdateContent(user) && timeExpired()) throw new IllegalOperationException("An error occurred, cannot update content.");
this.content = content;
markDirty();
}
}
At first glance, it looks marvelous, since you dont have to manually call insert, save and delete method on your repository/data mapper. However, I see two problems with this approach:
Tight coupling of domain model with Unit of work: This implementation of Unit of Work will make domain models dependent on UnitOfWork class. UnitOfWork has to come from somewhere, the implementation of static class/method is bad. To improve this, we need to switch to dependency injection, and pass an instance of UnitOfWork to the constructor of Domain Model. But this still couples domain model with Unit of work. Also ideally a domain model should only accept parameters for its data fields(ie. Message domain model's constructor should only accept whats relevant to message, such as title, content, dateposted, etc). If it will need to accept a parameter of UnitOfWork, it will pollute the constructor.
The domain model now becomes persistent-aware: In modern application design, especially DDD, we strive for persistent-ignorant model. The domain model shouldnt care about whether it is being persisted or not, it should not even care about whether there's persistence layer at all. By having those markNew(), markDirty(), etc methods on domain model, our domain models now have the responsibility of informing the rest of our application that it needs to be persisted. Although it does not handle the persistence logic, the model still is aware of the existence of persistence layer. I am not sure if this is a good idea, to me it seems to have violate the single responsibility principle. There's also an article talking about this: http://blog.sapiensworks.com/post/2014/06/04/Unit-Of-Work-is-the-new-Singleton.aspx/
So what do you think? Does the original Unit of Work pattern described in Martin Fowler violate good OO design principles? If so, do you consider it an antipattern?