DDD - 使用流程管理器或域服务

时间:2018-03-26 14:59:00

标签: domain-driven-design

我是DDD的新手,我正在我的应用程序的一部分上实现它,因为应用程序的一些要求引导我使用事件采购的CQRS(需要系统中发生的历史事件以及需要能够查看过去的系统状态。) 在阅读Vaughn Vernon书及其有效聚合设计系列之后,我遇到的一个问题是Process Manager(Long Running Porcess)和Domain Service之间的区别。特别是当您将聚合的导航属性转换为另一个聚合

我将解释我所理解的内容:
- 域服务用于保存不属于任何聚合的逻辑。根据Vaughn,它也可以用于将实体引用传递给包含它的聚合。它也可以用于管理事务,因为它们无法处理域对象
- 进程管理器用于协调对系统进行的修改,并跨越不同的聚合。有些人说好的Process Manager实际上是一个Aggregate Root。根据我的理解,它不会在事务在提交更改后启动时管理事务。它使用最终一致性的方法。最终所有的更改都会发生

现在,把一切都放在上下文中。我正在构建的应用程序的核心是处理包含它们自己的逻辑的节点树。我们需要能够将树节点添加到树中,当然还要创建这些节点。
我们需要能够知道那些节点发生了什么。即我们需要能够检索链接到节点的事件 此外,必须将对其中一个叶子(取决于修改类型)所做的修改复制到作为该节点的父节点的其他节点。

我的总量是什么:
- 节点,它是我的树包含的。在我看来,这是一个集合有几个原因。它不是一个不变量,因此不是一个有价值的对象。他们有自己的域逻辑,允许他们为它的属性分配它的属性,我们需要能够使用ID来访问它们 - 由节点组成的非二进制树的表示。现在,我实际上将其设计为我的聚合Root,它实际上是一个流程管理器。树包含此树的逻辑表示。它包含树的根。这个root实际上是一个Object(我不确定它是否可以被命名为Value Object,因为它包含对其他聚合的引用,Child Nodes,但它听起来确实如此)。树中的节点对象包含基本信息,如节点名称和对实际聚合的引用(这几乎听起来像是两个有界的上下文?)

使用这种方法,这就是发生的事情:
- 执行创建节点的命令后,将创建并提交节点。 NodeCreated事件被启动,由正确的Handler捕获,该Handler检索与此节点关联的Tree(进程管理器)并将节点添加到正确的位置(使用Node的parent id属性)
- 执行修改节点的命令后,将修改并提交节点。 NodeModified事件启动,由处理程序捕获。然后,处理程序检索树(我的进程管理器)并找到已修改节点的所有父节点,并要求这些节点根据子节点上修改的内容修改自己的属性。这一切都很有道理,看起来几乎是美丽的,展示了事件的力量和领域逻辑的分离

但是,我的主要问题在于交易。如果在更新树和必须修改或添加的节点时发生错误会发生什么? Node的事件已保存在Event Store中,因为它已提交。所以我必须创建一个新的事件来恢复修改?我知道命令在进入系统时必须是有效的,因此它不会成为验证问题,而且发生的事情可能就像百万分之一。这是否意味着我们不应该考虑这种可能性?

交易问题是我觉得我应该使用服务的原因。应用程序服务(此处为命令处理程序)或域服务,用于编排mofications并在单个事务中执行它们。如果在此事务期间出现故障,则不会创建/修改任何内容,但会破坏DDD规则,即我不应修改同一事务中的多个聚合。这在某种程度上看起来不太优雅的解决方案

我真的觉得我在这里遗漏了一些东西,但我不太确定它是什么。

2 个答案:

答案 0 :(得分:0)

  

我真的觉得我在这里遗漏了一些东西,但我不太确定它是什么。

您的域名模型可能过于复杂。

域服务通常是服务提供商,它使域模型可以访问(缓存)状态或通常不具备的功能。例如,我们可能会使用域服务为模型提供对缓存税表的访问权限,以便它可以计算订单的税;或者我们可以使用域服务为模型访问委派给电子邮件基础结构的notifyCustomer功能。

流程管理器通常用于编排 - 它们基本上是状态机,用于查看发生的事件(事件)并建议运行其他命令。请参阅Rinat Abdullin's description

  

如果在更新树和必须修改或添加的节点时发生错误,会发生什么? Node的事件已保存在Event Store中,因为它已提交。所以我必须创建一个新的事件来恢复修改?

也许 - 补偿事件是一种常见的模式。

重点是:在多个交易中编排变更没有任何魔力。考虑如何安排一个UI,向操作员显示正在进行的操作,接下来应该发生什么,以及失败模式将是什么。

  事情发生的可能性就像百万分之一。这是否意味着我们不应该考虑这种可能性?

取决于业务风险。但正如格雷格·扬在他的演讲Stop Over Engineering中指出的那样,如果你可以将一百万个问题中的1个问题提升为人类进行整理,那么你可能已经做得足够了。

答案 1 :(得分:0)

  

有些人说好的流程管理器实际上是一个聚合根

从我的观点来看,这是不正确的。流程管理器或Saga协调跨越多个Aggregate实例的长时间运行的业务流程。它使系统最终处于有效的最终状态。它不会发出事件但会响应事件并创建到达聚合的命令(可能通过命令处理程序,具体取决于您的确切体系结构)。那些表示未能正确识别聚合边界的架构师。

流程经理/佐贺可能是有状态的 - 但只是记住它所取得的进展;它可以有一个进程ID;它甚至可以是事件来源。

  

进程管理器用于协调对系统进行的修改,并跨越不同的聚合。

是的,这是正确的。

  

执行修改节点的命令后,将修改并提交节点。

在设计聚合时,必须仅考虑对架构的写/命令端存在的业务规则的不变量的保护;这是产生状态转换的一方,即事件驱动架构的事件发生。

我在您的特定情况下确定的单个业务规则(如果有)是,当节点创建(看起来像是CRUD操作!)时会发出NodeCreated事件;类似于NodeModified。因此,这些操作存在于写/命令端。

  

启动NodeModified事件,由处理程序捕获。然后,处理程序检索树(我的进程管理器)并找到已修改节点的所有父节点,并要求这些节点根据子节点上修改的内容修改自己的属性

有关更新Parents节点的写入方是否有任何业务规则?我什么也看不见。当然,在创建节点后会更新某些内容,但它不是聚合而是读取模型。您调用的处理程序实际上是一个Read模型。它在Node of Tree上投射NodeXXX事件。