问题描述
多个消息来源声称流程管理器不包含任何业务逻辑。例如,Microsoft article 是这样说的:
您不应使用流程管理器在您的域中实现任何业务逻辑。业务逻辑属于聚合类型。
此外,他们还这么说(强调我的):
需要注意的是,流程管理器不执行任何业务逻辑。它仅路由消息,并且在某些情况下在消息类型之间进行转换。
但是,我不明白为什么消息之间的转换(例如从域事件到命令)不是业务逻辑的一部分。您需要领域专家才能了解正确的步骤顺序以及它们之间的转换。在某些情况下,您还需要在步骤之间保持状态,您甚至可以根据某些(业务)条件选择下一步。因此,并非所有内容都是给定步骤的静态列表(尽管 我也将其称为业务逻辑)。
在许多方面,流程管理器(或就此而言的传奇)只是另一种保持状态的聚合类型,并且在我看来可能具有一些业务不变量。
假设我们使用六边形架构实现 DDD,我会将进程管理器放在应用层(不是适配器!!),这样它就可以对消息做出反应或由计时器触发。它将通过存储库加载相应的流程管理器聚合,并调用其上的方法来设置其(业务)状态或要求它发送下一个命令(实际发送由应用程序完成)当然是层)。此聚合位于域层中,因为它执行业务逻辑。
我真的不明白为什么人们要区分业务规则和工作流规则。如果您删除除领域层之外的所有内容,您应该能够重建一个工作应用程序,而无需再次咨询领域专家。
我很乐意从你们那里得到一些我可能遗漏的进一步见解。
解决方法
此处相当一部分混乱是 semantic diffusion 的结果。
“流程管理器”的拼写来自企业集成模式(Hohpe 和 Woolf,2003 年)。在那里,这是一个消息模式;更准确地说,它是消息路由器的一种可能的特化。消息路由器的动机是发送者和接收者的解耦。
如果定义了新的消息类型、添加了新的处理组件或更改了路由规则,我们只需要更改消息路由器逻辑,而所有其他组件不受影响。
在此上下文中,流程管理器是指位于中心辐射设计中间的消息路由器的特殊化,维护处理序列的状态并“根据中间结果确定下一个处理步骤”。
“流程定义”当然是企业关心的事情——毕竟,我们传递这些消息是为了协调企业不同部分的活动。
是的……这个维护“处理序列状态”的东西,听起来很像一个“域实体”的例子,这是真的。
BUT:是消息路由域的实体;也就是说,它是簿记以确保消息到达正确的位置,而不是簿记业务信息(即:运输集装箱的路线)。
用六边形架构的语言表达,流程管理器所做的是跟踪发送到其他六边形的消息(当然,还有它们发回的消息)。
,域逻辑不仅可以在聚合和域服务中找到。其他地方是:
- 适当地处理领域事件。领域事件可以翻译成一个或多个命令聚合;他们可以根据事件本身的某些条件/策略和/或其他聚合的状态触发这些命令;他们可以通知正在进行的业务流程以继续其下一步,等等。所有这些都是领域逻辑的一部分。
- 业务流程是一个(潜在的分布式)状态机,可能涉及各种参与者/用户/系统。允许的状态和它们之间的转换都是域逻辑的核心部分。
- saga 是一个最终一致的事务,跨越多个本地或外部聚合,成功完成或以最大努力的方式补偿已执行的步骤。构成传奇的步骤只能由领域专家知道,因此是领域逻辑的一部分。
在我看来,这三件事被误认为是仅应用层关注的原因如下:
- 为了处理领域事件,我们必须加载并稍后保存受影响的聚合。因此,处理程序必须也成为应用层的一部分。但至关重要的是,不仅仅是。如果我们尊重六边形架构背后的思想,那么对于驻留在应用层中的每个领域事件处理程序,在领域层中必须有一个对应的领域事件处理程序。 即使对于一个领域事件转化为某个聚合上的一个命令方法调用的最微不足道的情况。 这在许多示例中可能被省略,因为它最初增加的价值很小。但是想象一下,稍后翻译将基于一些进一步的业务条件。我们是否也将它放在应用程序层处理程序中?记住:所有我们的领域逻辑都应该在领域层。
- 附注:即使我们尊重这种关注点分离,我们仍然可以选择让域事件由聚合本身处理,或者让它由聚合命令转换为聚合命令瘦域服务。然而,这个选择是基于一个完全不同的问题:我们是否想要更紧密地耦合聚合。这里没有正确或错误的答案。有些事情自然而然地耦合得更紧密,而另一些事情可能会受益于一些额外的间接性以提高灵活性。
- 为了正确实施业务流程或传奇,我们必须处理各种特定于应用程序的问题,例如消息重复数据删除、幂等性、重试、超时、日志记录等。尽管有人可能会争辩说域逻辑本身应该负责至少直接处理其中一些方面(见Vaughn Vernons excellent talk about modelling uncertainty)。但是请记住,(允许的)步骤/操作序列的本质完全基于域逻辑。
最后,关于耦合。在我看来,社区中有一种趋势,即耦合本身是一件坏事,因此必须通过一切手段避免/减轻。这可能会导致解决方案,例如将事件命令转换(记住:域逻辑!)放在六边形/洋葱/干净架构的适配器层中。这个层的职责是将某些东西适应相同的语义/功能,但形式略有不同(想想电源适配器)。它不是托管任何类型的域逻辑的地方,即使它非常简单。企业到处都有依赖和耦合。艺术是在它实际存在的地方拥抱它,否则就避免它。我们在 DDD 中建立合作伙伴关系或客户/供应商关系是有原因的。如果我们关心领域逻辑隔离,那么这些依赖就会反映在它们所属的地方:在领域层。
- 旁注:反腐败层 (DDD) 是一个有效的适配器示例。例如,它可能需要一堆远程域事件并以任何必要的方式转换/组合它们以适合本地模型。它们仍然是过去发生的事件,而不仅仅是神奇地变成命令。转化只改变形式,不改变功能。从领域的角度来看,它并没有消除不可避免的耦合。它只是用略有不同的语言重新表述同一件事。