gpt4 book ai didi

domain-driven-design - 事件溯源重构

转载 作者:行者123 更新时间:2023-12-04 14:54:27 25 4
gpt4 key购买 nike

我一直在研究 DDD,并偶然发现了 CQRS 和事件源 (ES) 等设计模式。这些模式可用于帮助轻松实现 DDD 的某些概念。
在下面举例说明的体系结构中,聚合知道如何处理与自身相关的命令和事件。换句话说,事件处理程序和命令处理程序是聚合。

然后,我开始对一个示例域进行建模,以了解实现将如何遵循业务逻辑。对于这个问题,这里是我的域(它基于 this):

Example Domain

我知道这是一个糟糕的建模示例,但我仅将其用作示例。
因此,使用 ES,在操作结束时,我们会将所有事件(绿色箭头)保存到事件存储中(如果没有异常),每个事件都保存到其给定的事件流中(聚合类型 + 聚合 Id):

Event Streams

直到现在,一切似乎都是正确的。因此,如果我们想要重建任何此 Aggregate 实例的内部状态,我们只需要对其进行更新 (new()) 并以正确的顺序应用保存在其各自 Event Stream 中的所有事件。

Event Rebuild

我的问题与模型的变化有关。因为,软件开发是一个我们永远不会停止学习我们的领域的过程,我们总是带着新的想法来。那么,让我们分析一些变化场景:

更改场景 1:

现在假设,如果 预订汇总 检查座位不可用,它应该发送一个事件( 座位未保留 )并且该事件应该由一个新的聚合处理,该聚合将存储所有未保留座位的人:

First Change

假设旧系统已经正确处理了初始命令( 下订单 ),并将所有事件保存到各自的事件流中:

  • 当我们想要重建任何这个聚合的实例的内部状态时,我们只需要更新它 (new()) 并以正确的顺序应用保存在其各自事件流中的所有事件。 (没有改变)。唯一的问题是新用例在旧模型中不存在。

  • 更改场景 2:

    假设现在,当付款被接受时,我们在新的聚合 ( 财务聚合 ) 中而不是在 中处理此事件 ( 接受付款 )订单汇总 了。它向 发送一个新事件( 已收到付款 )订单汇总 .我知道这个场景的结构不是很好,但这样的事情可能会发生。

    Second Change

    假设旧系统已经正确处理了初始命令( 下订单 ),并将所有事件保存到各自的事件流中:
  • 当我们想要重建任何这个聚合的实例的内部状态时,我们在将来自聚合事件流的事件应用到自身时会遇到问题:

  • Error Rebuilding

    现在,订单不再知道如何处理 接受付款 事件。

    问题

    因此,如示例所示,每当系统更改反射(reflect)在由不同事件处理程序(聚合)处理的事件中时,就会出现一些主要问题。因为,我们不能再重建内部状态了。
    所以,这个问题可以有一些解决办法:

    可能的解决方案

    当一个事件没有被存储它的 Event Stream 的聚合处理时,我们可以找到新的处理程序并创建一个新实例并将事件发送给它。但是为了保持内部状态正确,我们需要最后一个事件( Payment Received )由 处理。订单汇总 .所以,我们让它调度事件(和可能的命令):

    Solution Rebuild

    此解决方案可能存在一些问题。让我们想象一个新命令( Place Order )到达,它必须创建这个订单实例并保存新状态。现在我们将有:

    New Streams

    灰色的是在系统尚未完成模型更改时已在上次调用中保存的事件。
    我们可以看到为新聚合 ( Finance W ) 创建了一个新的事件流。我们可以看到事件流是仅附加的,所以 接受付款 事件在 订单 Y 事件流仍然存在。
    第一 接受付款 事件在 财务W Event Stream 是应该由 处理的事件流。订购 但必须找到一个新的处理程序。
    中收到黄色付款事件订单事件流是由 的新处理程序生成的事件。接受付款 接受付款 事件来自 订单事件流由 处理财务 .
    所有其他绿色事件都是通过处理 生成的新事件。下单 新模型中的命令。

    解决方案的问题

    下一次需要重建聚合时,流中将有一个 Payment Accepted 事件(因为它是仅追加的),它将再次调用新的处理程序,但这已经完成,并且 Payment Received 事件有已经保存到流中。所以,没有必要再次经历这个,我们可以忽略这个事件并继续。



    所以,我的问题是我们如何处理影响谁处理每个事件的模型更改?在这样的更改之后,我们如何重建聚合的内部状态?
    我们是否需要构建一些事件流迁移,将事件从一个流更改为新模式(一个或多个流)?就像我们在关系数据库中需要的那样?
    我们永远不会被允许删除一个处理程序,所以我们只能添加新的处理程序吗?这将导致无法管理的系统......

    最佳答案

    你几乎一切都好,除了一件事:聚合不应处理来自其他聚合的事件 .这就像一个非事件源聚合与另一个聚合共享一个表:他们不应该。

    在事件驱动的 DDD 中,聚合是系统的构建块,用于接收命令(表达意图的事物)并返回事件(已发生的事物)。对于每个命令类型,必须存在一种且只有一种处理它的聚合类型。在执行命令之前,聚合被提供给它自己之前发出的所有事件,也就是说,这个聚合实例过去发出的每个事件都按时间顺序应用于这个聚合实例。

    因此,如果您想对系统进行正确建模,则不允许将来自一个聚合的事件作为事件发送到另一个聚合(不同类型或实例)。

    如果您需要对涉及多个聚合的业务流程建模,正确的做法是使用 Saga/流程经理 .这是一个不同的组件。它与聚合相反。
    它接收聚合发出的事件并将命令发送到其他聚合。

    在最简单的情况下,Saga 管理器只是从一个 Event 中获取属性并使用这些属性创建并填充一个 Command。然后它将命令发送到目标聚合。

    在更复杂的情况下,Saga 等待多个事件,当所有事件都收到时,它才会创建并发送一个命令。

    Saga 还可以对事件进行重复数据删除或重新排序。

    在您的情况下,Saga 可能是 Sale ,其目的是协调从订购到产品发货的整个销售流程。

    总结 ,你有这个问题,因为你没有正确地建模你的系统。如果你的聚合只处理它们的特定命令(而不是其他人的事件),那么即使你必须在新业务流程出现时创建一个新的 Saga,它也会将相同的命令发送到相同的聚合。

    关于domain-driven-design - 事件溯源重构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49804203/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com