DDD 流程经理:它们是业务逻辑的一部分吗?

DDD Process Managers: Are they part of business logic?

多个消息来源声称流程管理器不包含任何业务逻辑。例如 Microsoft article 表示:

You should not use a process manager to implement any business logic in your domain. Business logic belongs in the aggregate types.

再往上他们也这样说(强调我的):

It's important to note that the process manager does not perform any business logic. It only routes messages, and in some cases translates between message types.

但是,我不明白为什么消息之间的转换(例如从域事件到命令)不是业务逻辑的一部分。您需要领域专家才能了解正确的步骤顺序以及它们之间的翻译。在某些情况下,您还需要在步骤之间保持状态,您甚至可能 select 基于某些(业务)条件的后续步骤。因此,并非所有内容都是给定步骤的静态列表(尽管 单独我也称其为业务逻辑)。

在我看来,在许多方面,流程管理器(或 saga)只是另一种持久状态的聚合类型,并且可能具有一些业务不变性。

假设我们用六边形架构实现 DDD,我会将进程管理器放在应用层(而不是适配器!!),以便它可以对消息做出反应或由计时器触发。它将通过存储库加载相应的 进程管理器聚合 并调用其上的方法来设置其(业务)状态或要求它发送下一个命令(实际发送完成的地方)当然是应用层)。该聚合存在于域层中,因为它执行业务逻辑。

我真的不明白为什么人们会区分业务规则和工作流规则。如果您删除领域层以外的所有内容,您应该能够重建工作应用程序,而无需再次咨询领域专家。

我很乐意从你们那里得到一些我可能遗漏的进一步见解。

这里的混淆有相当一部分是由于 semantic diffusion

“流程管理器”的拼写来自 Enterprise Integration Patterns(Hohpe 和 Woolf,2003 年)。在那里,它是一个 消息传递模式 ;更准确地说,它是 消息路由器 的一种可能特化。消息路由器的动机是发送方和接收方的解耦。

If new message types are defined, new processing components are added, or routing rules change, we need to change only the Message Router logic, while all other components remain unaffected.

在此上下文中,进程管理器是指位于中心辐射设计中间的消息路由器的专门化,维护处理序列的状态并“根据中间结果确定下一个处理步骤” .

“流程定义”当然是企业关心的事情——毕竟我们正在传递这些消息以协调企业不同部分的活动。

是的......这个维护“处理序列状态”的东西,听起来很像“领域实体”的例子,这是真的。

BUT:它是消息路由域的一个实体;也就是说,它是记账以确保消息到达正确的位置,而不是记账业务信息(即:海运集装箱的路线)。

用六边形架构的语言表达,进程管理器所做的是跟踪消息发送到其他六边形(当然,还有它们发回的消息).

域逻辑不仅存在于聚合和域服务中。其他地方是:

  • 适当处理域事件。领域事件可以转化为一个或多个命令来聚合;他们可以根据一些 condition/policy 事件本身 and/or 其他聚合的状态来触发这些命令;他们可以通知正在进行的业务流程以继续其下一步,等等。所有这些都是域逻辑的一部分。
  • 一个业务流程是一个(潜在的分布式)状态机,可能涉及各种actors/users/systems。允许的状态和它们之间的转换都是领域逻辑的核心部分。
  • A saga 是跨越多个本地或外部聚合的最终一致事务,它成功完成或以尽力而为的方式补偿已经执行的步骤。构成传奇的步骤只能为领域专家所知,因此是领域逻辑的一部分。

在我看来,这三件事被误认为是仅应用层问题的原因如下:

  • 为了处理域事件,我们必须加载并稍后保存受影响的聚合。因此,处理程序必须 是应用程序层的一部分。但至关重要的是,不仅仅是 。如果我们尊重六边形架构背后的思想,那么对于驻留在应用层的每个领域事件处理程序,必须有一个位于领域层的对应事件处理程序。 即使是最微不足道的情况,其中一个域事件转换为对某个聚合的一个命令方法调用。这可能在许多示例中被省略,因为它最初增加的价值很小。但试想一下,稍后,翻译将基于一些进一步的业务条件。我们是否也将它放在应用程序层处理程序中?记住:我们领域逻辑的所有都应该在领域层。
    • 旁注:即使我们尊重这种关注点分离,我们仍然可以选择让领域事件由聚合本身处理或将其转换为瘦域服务的聚合命令。然而,这种选择是基于一个完全不同的问题:我们是否想要更紧密地耦合聚合。这里没有正确或错误的答案。有些事情自然而然地耦合得更紧密,而其他事情可能会受益于一些额外的间接性以提高灵活性。
  • 为了正确实施业务流程或传奇,我们必须处理各种特定于应用程序的问题,例如消息重复数据删除、幂等性、重试、超时、日志记录等。尽管有人可能会争辩说领域逻辑本身应该负责处理至少其中一些方面的第一手资料(参见 Vaughn Vernons excellent talk about modelling uncertainty)。但是请记住,(允许)steps/actions 序列的本质完全基于领域逻辑。

最后说一下耦合。在我看来,社区中有一种趋势,耦合本身是一件坏事,因此必须 avoided/mitigated 无论如何。这可能会导致解决方案,例如将事件命令转换(记住:域逻辑!)放在 hexagonal/onion/clean 架构的适配器层中。这一层的职责是使某些东西适应具有 相同 semantics/function 但略有 不同形式 的其他东西(想想电源适配器)。它不是托管任何类型的域逻辑的地方,即使它非常简单。企业到处都有依赖和耦合。艺术是在它实际存在的地方拥抱它,并在其他地方避免它。我们在 DDD 中建立伙伴关系或 customer/supplier 关系是有原因的。如果我们关心域逻辑隔离,那么这些依赖关系就会反映在它们所属的位置:在域层中。

  • A 旁注反腐败层 (DDD) 是适配器的有效示例。例如,它可能需要一堆远程域事件和 transform/combine 它们以适合本地模型的任何必要方式。它们仍然是过去发生的事件,而不是神奇地变成命令。转型只改变形式,不改变功能。从领域的角度来看,它并没有消除不可避免的耦合。它只是用稍微不同的语言改写了同一件事。