我刚开始学习DDD,并在应用程序的一部分实现它,因为应用程序的某些要求导致我使用CQRS和事件溯源(需要记录系统中发生的事件的历史记录以及需要能够查看过去系统的状态)。
阅读Vaughn Vernon的书籍和其Effective Aggregate Design系列后,我有一个问题是:Long Running Process(流程管理器)和Domain Service(领域服务)之间有什么区别,特别是当您将导航属性指向另一个聚合时。
我理解的是:
- 领域服务用于保存不属于任何聚合的逻辑。根据Vaughn的说法,它也可以用于将实体引用传递到包含它的聚合中。它还可以用于管理事务,因为无法在域对象中处理它们。
- 流程管理器用于编排对系统进行的修改,并跨不同的聚合范围。有些人说,一个好的流程管理器实际上是一个聚合根。从我的理解来看,它不会管理事务,因为事件是在提交更改后启动的。它采用最终一致性的方法。最终所有的更改都将发生。
现在,为了将所有内容放入上下文中。我正在构建的应用程序的核心是处理包含其自己逻辑的节点树。我们需要能够向树中添加节点,当然也需要创建这些节点。
我们需要能够知道这些节点发生了什么。即,我们需要能够检索与节点相关联的事件。
还有一种对叶子节点进行的修改(取决于修改的类型)必须被复制到此节点的父节点中的其他节点。
我的聚合是什么:
- Nodes,它是我的树所包含的内容。在我看来,这是一个聚合,原因有几个。它不是不变量,因此不是值对象。它们具有自己的领域逻辑,允许它们为其属性分配值对象,并且我们需要能够使用ID访问它们。
- 由节点组成的非二叉树的表示。现在,我实际上将其设计为我的聚合根,它实际上是一个流程管理器。该树包含此树的逻辑表示。它包含树的根。这个根实际上是一个对象(我不确定它是否可以称为值对象,因为它包含对其他聚合的引用,子节点,但它肯定听起来像是)。树中的节点对象包含基本信息,如节点名称和指向实际聚合的引用(这几乎听起来像两个有界上下文?)
使用这种方法,以下是正在发生的事情:
- 执行创建Node的命令后,会创建并提交一个Node。NodeCreated事件被触发,被正确的处理器捕获,该处理器检索与此节点关联的Tree(进程管理器)并将节点添加到正确的位置(使用Node的父ID属性)
- 执行修改Node的命令后,节点被修改并提交。NodeModified事件被触发,被处理器捕获。然后,处理器检索Tree(我的进程管理器)并查找所有修改的节点的父节点,并请求这些节点根据Child Node上的修改来修改它们自己的属性。所有这些都很有道理,对我来说看起来几乎很漂亮,展示了事件的力量和域逻辑的分离
阅读Vaughn Vernon的书籍和其Effective Aggregate Design系列后,我有一个问题是:Long Running Process(流程管理器)和Domain Service(领域服务)之间有什么区别,特别是当您将导航属性指向另一个聚合时。
我理解的是:
- 领域服务用于保存不属于任何聚合的逻辑。根据Vaughn的说法,它也可以用于将实体引用传递到包含它的聚合中。它还可以用于管理事务,因为无法在域对象中处理它们。
- 流程管理器用于编排对系统进行的修改,并跨不同的聚合范围。有些人说,一个好的流程管理器实际上是一个聚合根。从我的理解来看,它不会管理事务,因为事件是在提交更改后启动的。它采用最终一致性的方法。最终所有的更改都将发生。
现在,为了将所有内容放入上下文中。我正在构建的应用程序的核心是处理包含其自己逻辑的节点树。我们需要能够向树中添加节点,当然也需要创建这些节点。
我们需要能够知道这些节点发生了什么。即,我们需要能够检索与节点相关联的事件。
还有一种对叶子节点进行的修改(取决于修改的类型)必须被复制到此节点的父节点中的其他节点。
我的聚合是什么:
- Nodes,它是我的树所包含的内容。在我看来,这是一个聚合,原因有几个。它不是不变量,因此不是值对象。它们具有自己的领域逻辑,允许它们为其属性分配值对象,并且我们需要能够使用ID访问它们。
- 由节点组成的非二叉树的表示。现在,我实际上将其设计为我的聚合根,它实际上是一个流程管理器。该树包含此树的逻辑表示。它包含树的根。这个根实际上是一个对象(我不确定它是否可以称为值对象,因为它包含对其他聚合的引用,子节点,但它肯定听起来像是)。树中的节点对象包含基本信息,如节点名称和指向实际聚合的引用(这几乎听起来像两个有界上下文?)
使用这种方法,以下是正在发生的事情:
- 执行创建Node的命令后,会创建并提交一个Node。NodeCreated事件被触发,被正确的处理器捕获,该处理器检索与此节点关联的Tree(进程管理器)并将节点添加到正确的位置(使用Node的父ID属性)
- 执行修改Node的命令后,节点被修改并提交。NodeModified事件被触发,被处理器捕获。然后,处理器检索Tree(我的进程管理器)并查找所有修改的节点的父节点,并请求这些节点根据Child Node上的修改来修改它们自己的属性。所有这些都很有道理,对我来说看起来几乎很漂亮,展示了事件的力量和域逻辑的分离
但是,我的主要问题在于事务。如果在更新Tree和需要修改或添加的节点时出现错误会发生什么?因为已经提交了Node的事件,所以必须创建新的事件来恢复修改吗?我知道输入系统时命令必须有效,因此这不是验证问题,而且出现问题的可能性就像一百万分之一。这是否意味着我们不应考虑这种可能性?
事务问题正是我感觉应该使用服务的原因。无论是应用程序服务(此处为命令处理程序)还是域服务,都可以进行编排修改并在单个事务中完成。如果在此过程中出现故障,则不会创建/修改任何内容,但这违反了DDD规则,即我不应在同一交易中修改多个聚合。这看起来有些不太优雅的解决方案
我真的觉得自己在这方面缺少了什么,但我不确定究竟是什么。