从POEAA一书中,Martin Fowler介绍了这个工作单元的概念。如果你想拥有自动提交系统,你的域模型使用工作单元将自己标记为新的,脏的,删除的或清理的,它可以很好地工作。然后,您只需要调用UnitofWork.commit(),即可保存所有模型更改。以下是具有此类方法的域模型类:
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);
}
}
通过此实现,您可以通过业务逻辑方法将域模型标记为任何保存状态:
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();
}
}
乍一看,它看起来很奇妙,因为您不必在存储库/数据映射器上手动调用insert,save和delete方法。但是,我发现这种方法存在两个问题:
域模型与工作单元的紧密耦合:工作单元的这种实现将使域模型依赖于UnitOfWork类。 UnitOfWork必须来自某个地方,静态类/方法的实现很糟糕。为了改善这一点,我们需要切换到依赖注入,并将UnitOfWork的实例传递给Domain Model的构造函数。但这仍然将领域模型与工作单元联系起来。同样理想情况下,域模型应该只接受其数据字段的参数(即,消息域模型的构造函数应该只接受与消息相关的内容,例如标题,内容,日期发布等)。如果它需要接受UnitOfWork的参数,它将污染构造函数。
域模型现在变得持久感知:在现代应用程序设计中,特别是DDD,我们努力寻找持久无知的模型。域模型不应该关心它是否被持久化,它甚至不应该关心是否存在持久层。通过在域模型上使用那些markNew(),markDirty()等方法,我们的域模型现在有责任通知我们应用程序的其余部分需要持久化。虽然它不处理持久性逻辑,但模型仍然知道持久层的存在。我不确定这是不是一个好主意,对我而言似乎违反了单一责任原则。还有一篇文章谈到这个: http://blog.sapiensworks.com/post/2014/06/04/Unit-Of-Work-is-the-new-Singleton.aspx/
那你觉得怎么样? Martin Fowler中描述的原始工作单元模式是否违反了良好的OO设计原则?如果是这样,你认为它是反模式吗?
答案 0 :(得分:7)
为了完全准确,没有人“Martin Fowler实施工作单位”。在书中,他区分了两种类型的修改对象注册为UoW。
来电者注册,其中只有调用对象知道UoW,并且必须将(被调用者)域对象标记为脏。 据我所知,没有反模式或不良做法。
对象注册,域对象向UoW注册自己。这里有两个选择:
要使这个计划有效,工作单位需要传递给 对象或在一个众所周知的地方。通过工作单位 周围是乏味的,但通常没有问题让它出现在一些 会话对象。
代码示例正在使用UnitOfWork.GetCurrent()
,它更接近后一个选项,并且因为紧密耦合的隐式依赖(服务定位器样式)而被公认为今天的反模式。
但是,如果选择了第一个选项,即将UoW传递给域对象,让我们假设一个工作单元抽象,这是不好的做法?从依赖管理的角度来看,显然不是。
现在仍然是持久性无知方面。我们可以说一个对象可以发出另一个对象的信号,它刚刚被编辑/创建/删除,它是持久性 - 意识到?非常值得商榷。 相比之下,如果我们查看更新的域对象实现,例如事件源中的实现,我们可以看到聚合can be responsible for keeping a list of their own uncommitted changes或多或少相同的想法。这会违反持久性无知吗?我不这么认为。
底线:Fowler选择说明其中一种UoW可能性的具体代码现在显然被认为是不好的做法,但对于您指出的问题#1更是如此,而不是真正的问题#2。这并没有取消他撰写的其他实现的资格,也没有取消整个UoW模式,其变更跟踪机制无论如何大部分时间都隐藏在第三方库魔术(如果读取:ORM)中,并且不像书中的例子那样硬编码。
答案 1 :(得分:3)
DDD包含以下规则:
应用程序服务应该只修改每个事务的一个聚合。
如果您遵循此规则,则可以清楚在应用服务操作期间更改了哪个聚合。然后,需要将此聚合传递到存储库以保存到DB:
repository.update(theAggregate);
无需其他电话。这会以你描述的形式击败模式的收益。
另一方面,您描述的模式引入了从域到持久性机制的依赖关系(取决于设计是真正的依赖还是仅仅是概念依赖)。 现在这是你应该避免的事情,因为它会大大增加模型的复杂性(不仅在内部,也为客户提供)。
因此,您不应该将这种形式的模式与DDD一起使用。
话虽如此,我认为这种模式是某个问题的众多解决方案之一。该解决方案有利有弊,其中一些您在问题中描述。在某些情况下,模式可能是最好的权衡,所以
不,这不是反模式。
答案 2 :(得分:0)
我不认为该模型不应该依赖于UoW。它更像是依赖于UoW的存储库,反过来,存储库将取决于模型。
如果你的存储库只依赖于一个抽象的UoW,那么唯一知道持久性技术的难题就是具体的UoW。
我倾向于允许模型依赖的唯一类是模型的其他部分:域服务,工厂等。