首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >马丁·福勒( Martin )的POEAA实现工作单元是一种反模式吗?

马丁·福勒( Martin )的POEAA实现工作单元是一种反模式吗?
EN

Stack Overflow用户
提问于 2015-10-14 07:38:34
回答 3查看 3.8K关注 0票数 4

从POEAA一书中,Martin介绍了工作单位的概念。如果您想拥有自动提交系统,那么它运行得非常好,在这个系统中,您的域模型使用“工作单元”来标记自己为新的、脏的、删除的或干净的。然后只需调用UnitofWork.commit(),模型的所有更改都将被保存。下面是一个具有以下方法的域模型类:

代码语言:javascript
复制
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);
    }
} 

使用此实现,您可以通过业务逻辑方法将域模型标记为任意保存状态:

代码语言:javascript
复制
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();
    }  
}

乍一看,它看起来非常棒,因为您不必手动调用存储库/数据映射程序上的插入、保存和删除方法。然而,我认为这种方法有两个问题:

  1. 领域模型与工作单元的紧密耦合:这种工作单元的实现将使域模型依赖于UnitOfWork类。UnitOfWork必须来自某个地方,静态类/方法的实现是不好的。为了改进这一点,我们需要切换到依赖注入,并将UnitOfWork的一个实例传递给域模型的构造器。但这仍然是将域模型与工作单元耦合在一起的。此外,理想情况下,域模型应该只接受其数据字段的参数(即。message域模型的构造函数应该只接受与消息相关的内容,如标题、内容、日期发布等)。如果它需要接受UnitOfWork的一个参数,它将污染构造函数。
  2. 领域模型现在变得持久感知:在现代应用程序设计中,特别是DDD,我们努力争取持久无知的模型。域模型不应该关心它是否被持久化,它甚至不应该关心是否存在持久层。通过在域模型上使用这些markNew()、markDirty()等方法,我们的域模型现在有责任通知我们的应用程序的其余部分需要持久化。虽然它不处理持久化逻辑,但模型仍然意识到持久性层的存在。我不知道这是否一个好主意,对我来说,这似乎违反了单一责任原则。还有一篇文章谈到这一点:http://blog.sapiensworks.com/post/2014/06/04/Unit-Of-Work-is-the-new-Singleton.aspx/

你怎么看?Martin中描述的最初的工作单元模式是否违反了良好的OO设计原则?如果是的话,你认为这是反模式吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-10-16 21:35:17

准确地说,没有人“马丁·福勒执行工作股”。在这本书中,他区分了将修改后的对象注册为UoW的两种类型。

Caller注册,其中只有调用对象知道UoW,并且必须将(被调用的)域对象标记为脏。据我所知,这里没有反模式或不良做法。

对象注册,其中域对象向UoW注册自己。这里还有两种选择:

要使这一计划发挥作用,工作单位要么需要传递给对象,要么需要在一个众所周知的地方。传递工作单元是乏味的,但在某种类型的会话对象中传递它通常是没有问题的。

代码示例使用的是UnitOfWork.GetCurrent(),它更接近后一种选项,而且由于紧密耦合的隐式依赖(Service样式),目前被公认为反模式。

但是,如果选择了第一个选项,即将UoW传递给域对象,让我们假设一个工作抽象单元,这会是一种糟糕的实践吗?从依赖性管理的角度来看,显然不是。

现在仍然是坚持无知的一面。我们能不能说一个对象可以向另一个对象发出信号--它刚刚被编辑/创建/删除--它是持久性感知的?有很大争议。相比之下,如果我们查看最近的领域对象实现,例如在事件源中的实现,我们可以看到聚合可以负责保存自己未提交的更改的列表。,这或多或少是相同的想法。这是否违反了坚持无知?我不这样认为。

:Fowler选择了具体的代码来说明许多UoW的可能性之一,这显然被认为是一种糟糕的实践,但对于您指出的问题#1,而不是真正的问题#2,情况就更糟了。而且这并不会取消他所写的其他实现的资格,也不会取消他所写的整个UoW模式,它的更改跟踪机制大部分时间都隐藏在第三方库魔术(读: ORM)中,而不是像书中的例子那样硬编码。

票数 8
EN

Stack Overflow用户

发布于 2015-10-15 06:32:15

从DDD的角度来看,这是你不应该做的事情。

DDD包含以下规则:

应用程序服务应该只修改每个事务的一个聚合。

如果您遵循这条规则,那么在应用程序服务操作期间,可以清楚地看到哪些聚合发生了变化。然后,需要将该聚合传递给存储库,以便保存到DB:

代码语言:javascript
复制
repository.update(theAggregate);

不需要打其他电话。这与你所描述的模式的增益相去甚远。

另一方面,您描述的模式将依赖关系从域引入到持久性机制(取决于设计过程中的实际依赖或概念依赖)。--这是您应该避免的事情,因为它大大增加了模型的复杂性(不仅在内部,对于客户也是如此)。

因此,您不应该将此表单中的模式与DDD一起使用。

DDD外

话虽如此,我认为这种模式是解决某个问题的许多解决方案之一。这个解决方案有优点也有缺点,你在问题中会描述其中的一些。在某些情况下,这种模式可能是最好的选择,所以

不,这不是反模式,

票数 6
EN

Stack Overflow用户

发布于 2015-10-14 07:55:54

我不认为模型不应该依赖于UoW。它更像是一个依赖于UoW的存储库,而存储库又将依赖于模型。

如果您的存储库只依赖于抽象的UoW,那么了解持久性技术的唯一谜团就是具体的UoW。

我倾向于允许模型依赖的唯一类是模型的其他部分:域服务、工厂等。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33119379

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档