域驱动设计 – 事件源代码重构

域驱动设计 – 事件源代码重构,第1张

概述我一直在研究DDD,偶然发现了CQRS和事件采购(ES)等设计模式.这些模式可用于帮助实现DDD的一些概念,而不需要花费太多精力. 在下面举例说明的体系结构中,聚合知道如何处理与其自身相关的命令和事件.换句话说,事件处理程序和命令处理程序是聚合. 然后,我开始建模一个示例域,只是为了理解实现如何遵循业务逻辑.对于这个问题,这里是我的域名(它基于this): 我知道这是一个错误的建模示例,但我只是作 我一直在研究DDD,偶然发现了CQRS和事件采购(ES)等设计模式.这些模式可用于帮助实现DDD的一些概念,而不需要花费太多精力.
在下面举例说明的体系结构中,聚合知道如何处理与其自身相关的命令和事件.换句话说,事件处理程序和命令处理程序是聚合.

然后,我开始建模一个示例域,只是为了理解实现如何遵循业务逻辑.对于这个问题,这里是我的域名(它基于this):

我知道这是一个错误的建模示例,但我只是作为一个例子使用它.
因此,使用ES,在 *** 作结束时,我们会将所有事件(绿色箭头)保存到事件存储中(如果没有异常),将每个事件保存到其给定的事件流(聚合类型聚合ID)中:

一切似乎都是正确的.因此,如果我们想要重建任何此Aggregate实例的内部状态,我们只需要新建它(new())并以正确的顺序应用保存在各自事件流中的所有事件.

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

更改场景1:

我们假装现在,如果预订聚合检查座位不可用,它应该发送一个事件(座位未保留),这个事件应由一个新的聚合处理,该聚合将存储所有未获得座位的人:

在旧系统已正确处理初始命令(放置顺序)并将所有事件保存到其各自事件流的假设中:

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

更改场景2:

让我们假装现在,当付款被接受时,我们将在新的Aggregate(财务汇总)中处理此事件(已接受付款),而不再在订单汇总中处理.它会向订单总计发送一个新事件(已收到付款).我知道这种情况结构不合理,但这种情况可能会发生.

在旧系统已正确处理初始命令(放置顺序)并将所有事件保存到其各自事件流的假设中:

>当我们想要重建任何此Aggregate的实例的内部状态时,我们在将聚合事件流中的事件应用于自身时遇到问题:

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

问题

因此,如示例所示,每当系统更改反映在由不同事件处理程序(Aggregate)处理的事件中时,存在一些主要问题.因为,我们不能再重建内部状态了.
所以,这个问题可以有一些解决方案:

可能解决方案

当事件未由存储事件流的聚合处理时,我们可以找到新处理程序并创建新实例并将事件发送给它.但是为了保持内部状态正确,我们需要最后一个事件(Payment Received)由Order Aggregate处理.所以,我们让它调度事件(以及可能的命令):

该解决方案可能存在一些问题.让我们假设一个新命令(Place Order)到了,它必须创建这个订单实例并保存新状态.现在我们有:

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

解决方案有问题

下次需要重建聚合时,流中会有一个支付接受事件(因为它只是追加),它会再次调用新的处理程序,但这已经完成,并且付款接收事件已经完成已经保存到流中.所以,没有必要再次通过这个,我们可以忽略这个事件并继续.

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

解决方法 几乎没有问题,除了一件事:聚合不应该处理来自其他聚合的事件.它就像一个非事件源的Aggregate与另一个Aggregate共享一个表:它们不应该.

在事件驱动的DDD中,Aggregates是系统的构建块,它接收命令(表达意图的东西)并返回事件(已发生的事情).对于每个Command类型,必须存在一个且只有一个处理它的Aggregate类型.在执行命令之前,Aggregate会提供其自己以前发出的所有事件,也就是说,此Aggregate实例过去发出的每个事件都按时间顺序应用于此Aggregate实例.

因此,如果要对系统进行正确建模,则不允许将事件从一个Aggregate作为事件发送到另一个Aggregate(另一个类型或实例).

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

在最简单的情况下,Saga管理器只需从一个事件中获取属性,并使用这些属性创建一个Command.然后它将Command发送到目标Aggregate.

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

佐贺也可能对事件进行重复数据删除或重新排序.

在您的情况下,Saga可能是Sale,其目的是协调整个销售过程,从订购到产品调度.

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

总结

以上是内存溢出为你收集整理的域驱动设计 – 事件源代码重构全部内容,希望文章能够帮你解决域驱动设计 – 事件源代码重构所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/1076534.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-26
下一篇2022-05-26

发表评论

登录后才能评论

评论列表(0条)

    保存