【问题标题】:How does process manager keep track of the association between aggregates流程管理器如何跟踪聚合之间的关联
【发布时间】:2017-07-24 20:56:33
【问题描述】:

流程管理器是否使用相关 ID 或聚合特定标识来跟踪它正在管理的流程

为了更清楚地举个例子,请看Saga on Sagas 上的图 2:

首先,进程管理器发送OrderConfirmed 事件是错误的,对吧?我(作为流程管理器)不能发送事件,只能发出命令。还是我错了?

其次,流程管理器如何关联来自不同聚合的 OrderCreated、SeatsReserved、PaymentReceived 事件?是每个聚合兑现(并复制)的相关 ID,还是特定的标识符(例如 SeatsReserved 有一个指向订单聚合的订单 ID)?

最后,如果是相关 id 的情况,谁创建它们?是客户端发出命令,例如PlaceOrder(order_id, correlation_id)?是接受PlaceOrder(order_id) 之类的命令然后发出OrderCreated(order_id, corr_id) 事件的聚合吗?或者,是流程经理(以某种方式)对此负责吗?或者,也许相关 ID 与此无关?

感谢您的帮助。

【问题讨论】:

    标签: domain-driven-design cqrs event-sourcing


    【解决方案1】:

    首先我应该说我的答案基于我的知识、经验和我对如何在 PHP 上进行 CQRS 的看法

    首先,Process Manager 发送 OrderConfirmed 事件是错误的,对吧?我(作为流程管理器)不能发送事件,只能发出命令。还是我错了?

    是的。这是错误的。只有Aggregates 引发Domain Events 并执行Commands。也许微软忘记显示发送到OrderAggregateMarkOrderAsConfirmed command

    其次,流程管理器如何关联来自不同聚合的 OrderCreated、SeatsReserved、PaymentReceived 事件?是每个聚合兑现(并复制)的相关 ID,还是特定的标识符(例如 SeatsReserved 有一个指向订单聚合的订单 ID)?

    我使用参与该过程的Aggregates 的ID。本例中的关联ID与OrderAggregate的ID一一对应,生命周期相同。

    最后,如果是相关 id 的情况,谁来创建它们?是客户端发出命令,例如 PlaceOrder(order_id, correlation_id)?是接受像 PlaceOrder(order_id) 这样的命令然后发出 OrderCreated(order_id, corr_id) 事件的聚合吗?或者,是流程经理(以某种方式)对此负责吗?或者,也许相关 ID 与此无关?

    在这种情况下,我不会使用相关 ID。我仅将它们用于调试目的。 在我的架构中,为了消除代码重复,相关 ID 由命令的创建者(在 Saga 中)CommandDispatcher 生成(如果这是进程中的第一个命令和所有Commands 一样,不是由Sagas 生成的),并以Metadata 的形式存储在生成的事件中。

    更新: 如何使用相关 ID?您可以从事件存储中选择具有该关联 ID 的所有事件,这样您就可以“看到”该过程。

    【讨论】:

    • 谢谢。对于最后一部分,聚合是否需要知道相关 ID?例如。如果ReservationAggregate 收到MakeReservation(reservation_id, num_tickets, correlation_id) 之类的命令,是否需要将关联ID 复制到ReservationMade(reservation_id, num_tickets, correlation_id) 事件中?
    • @drozzy 在我的架构中,我将关联 ID 保留在域层之外。它们由CommandDispatcher 从命令复制到事件,Sagas 从事件复制到命令。 CommandDispatcherSagas 不是来自域层。
    • 只有 X 可以发出事件而 Y 不能 - 这是非常教条的。我认为人们需要构建有效的东西,而不是盲目地遵循书籍而不了解他们在做什么。这一点很重要——停止做货物崇拜,用你自己的大脑来弄清楚是否可以使用某些东西。
    • @AlexeyZimarev 你有权发表意见,我尊重这一点,但在 DDD 中有一些好的规则可以遵循。这是其中之一。聚合是确保最低级别一致性的构建块。他们是独立的。他们拥有自己的数据。他们不需要来自外部的数据来做出决定。忽略这一点就是不遵循 DDD,这还不错,但它不是 DDD。感谢您的反馈(真的!)。
    • 是的,“这不是 DDD”是相当苛刻的。你可以向 Eric Evans 询问这件事,他不会同意你的看法,我保证。回到正题——你搞砸了域事件和集成事件。事件是一个通用术语。是的,Sagas 不会发出域事件,这很愚蠢。除非 Saga 本身就是一个聚合体。在任何情况下,enything 都可以发出集成事件。
    【解决方案2】:

    免责声明:由于您提到的示例交替使用“saga”和“流程管理器”术语,我也会这样做。我确实知道 saga 和流程管理器模式在定义上是不同的,但是在业界我们看到 saga 这个词被许多流行的框架(从 NServiceBus 开始)应用。

    看看这个特定的例子,它会是:

    • 从传奇中发布事件:这取决于。我强烈警告你不要对谁可以做什么建立任何教条主义的方法。你永远无法知道第二天会发生什么,你不应该只是“按部就班”做所有事情。如果 ProcessManager 协调整个过程,它很可能会从一个聚合或上下文中消耗OrderConfirmed 事件,发送命令到下一步,等待它产生确认事件,当整个过程完成时 - 发出它的自己的事件,在这种情况下,它通过一些 UI 魔术发送给客户。在这种方法中,我没有看到任何错误。不过可能有更好的解决方案。

    • 通常,saga id == 相关 id 并且它是一些人为确定的短暂寿命,在这种情况下,您确实需要跟踪相关性,或者依靠您的基础设施将其从命令传递到命令处理程序端的事件。您需要在创建 saga 实例后立即生成这样的 id(请记住,saga 具有逻辑和状态,并且每个正在运行的进程都有自己的状态)。或者您可以使用一些域对象 ID,例如在您所指的情况下,这可能是订单 ID。尽管如此,您仍需要注意将相关 id 从命令传递到命令处理程序端的事件。所以,saga id == correlation id == process state id,也经常作为saga状态持久化的主键。

    您还可以在Chris Patterson's blog post 中找到一篇关于使用状态机和 MassTransit 框架实现长时间运行流程的精彩博文。

    【讨论】:

      【解决方案3】:

      我将流程管理器视为我领域中的一等公民。流程管理器实例的Id 通常充当相关 ID。由于流程管理器是一个状态机,它实际上可以发布事件。好吧,我认为 域事件系统事件 不同。消息传递基础架构依赖于系统事件。这些是带有相关 id 的那些,是的,它会被复制到相关消息中,但这是您的基础设施可以自动执行的操作。在我的Shuttle.Esb 服务总线中,我就是这样做的:复制标头和相关 ID。

      这就是为什么我也认为事物的过程方面本身几乎是一种 BC。它通过发出命令然后响应相关事件与构成流程一部分的各种 BC 交互。但在基础设施(端点)层上,我可能想发布一个事件,说明某个特定流程已被放弃、完成或从一个步骤移至下一步。

      编辑

      在我看来,流程管理器本身也是一个聚合体。所有命令和事件都来自消息处理程序/应用程序层,以响应域中发生的事情。任何确实来自域的事件,就其本质而言,都是域事件,这些不一定适合系统到系统的通信。我想说“在集成/应用层”比在基础设施(端点)层上更合适。

      所有消息都在消息处理程序中处理。该消息处理程序充当集成点,必须直接或通过应用层与域交互。我的措辞可能并不明显:)

      【讨论】:

      • 谢谢,您能否澄清一下“在基础设施层”想要发布事件的含义?如果进程放弃某些东西,它会不会向相关聚合发出命令或被另一个进程管理器拾取(例如由于超时)?
      • 多么有趣的观点!我从不认为这个过程是一个独立的有界上下文,有它自己的关注点。
      【解决方案4】:

      强烈建议阅读Process Manager 设计模式的原始来源以及@Vaughn Vernon 在他的好书Implementing Domain-Driven Design 中对该主题的处理

      简而言之,流程管理器通过在启动特定流程时创建的流程实例同时处理多个流程。 Process 实例的 ID 是您的 Correlation Identifier,它是进程期间每个通信(命令/事件)的有效负载的一部分。 Process 实例还有另一个术语,即 Process Tracker。思路是一样的。

      因此,在这种方法中,关注点明显分离。每个 Process Instance 对其当前状态、重试、完成等方面负责自己的进程。它是一个 Process Instance,负责发出 ProcessFinished 事件,因为它是“大脑”。

      进程管理器的另一个名称是长期运行进程。所以根据定义,最好是异步的。

      ~谢尔盖

      【讨论】:

      • 谢谢,我都读过。但我无法真正理解相关标识符如何在聚合中发挥作用。您的意思是,相关标识符是一个概念,而不是事件和命令的显式字段,对吧?
      • @drozzy 相关标识符是命令/事件有效负载的一部分 - 显式字段。它是解决相关分布式工作流程步骤问题的模式/概念。相关标识符的性质是次要的。它的唯一要求是独一无二。
      • @drozzy 我应该提到,相关标识符不是 ddd 模型的一部分,而是命令/事件派生的消息抽象的一部分。
      • @SergiyChernets 我不明白的是这个。当某些命令/事件导致对完全不同的聚合执行操作时,例如 CloseOrder()Order 聚合执行操作。然后Order 聚合将引发OrderClosed 事件。 CorrelationId 在该事件中如何结束?必须为聚合上的每个方法添加一个 CorrelationId 似乎很难看...
      猜你喜欢
      • 1970-01-01
      • 2010-10-30
      • 1970-01-01
      • 2013-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-01
      • 1970-01-01
      相关资源
      最近更新 更多