如何在事件源中实施流程管理器


14

我正在研究一个小示例应用程序,以学习CQRS和事件源的概念。我有一个Basket汇总和一个Product应该独立工作的汇总。

这是一些伪代码来展示实现

Basket { BasketId; OrderLines; Address; }

// basket events
BasketCreated { BasketId; }
ItemAdded { BasketId; ProductId; Quantity }
AddItemSucceeded { BasketId; ProductId; Quantity }
AddItemRevoked { BasketId; ProductId; Quantity }
ItemRemoved { BasketId; ProductId; Quantity }
CheckedOut { BasketId; Address }

Product { ProductId; Name; Price; }

// product events
ProductReserved { ProductId; Quantity }
ProductReservationFailed { ProductId; Quantity }
ProductReservationCancelled { ProductId; Quantity; }

命令与事件非常相似,使用命令性名称而不使用过去式。

现在,这些独立工作就可以了。我发出一个命令AddItem,它ItemAddedBasket聚合上创建一个事件,该事件完成了对“篮子”状态的处理。同样,对于产品,命令和事件也可以正常工作。

我现在想将其组合成一个过程,该过程将是这样的(就发生的命令和事件而言):

流程经理将执行以下操作:

on BasketCreated: CreateShoppingProcess
on ItemAdded: ReserveProduct
on ProductReserved: SucceedAddingItem // does nothing, but needs to be there so that the basket knows it can check out
on ProductReservationFailed: RevokeAddItem
on RemoveItem: CancelProductReservation
on Checkout: CreateOrder // create an order and so on...

我找不到明确答案的问题是:

  1. 我是否需要坚持流程经理?看起来像我,但我不确定
  2. 如果这样做,则需要为流程管理器保存事件。但是,它正在监听的事件与聚合相关。是否向其中添加进程ID?我是否有仅针对流程经理的单独事件?如何做到这一点并尽可能保持干燥
  3. 我怎么知道这些ProductReserved活动是为了什么?可以在BasketId上面也可以,还是泄漏信息?
  4. 如何保持事件之间的关系,如何知道ItemAdded哪个ProductReserved事件产生了?我会经过EventId吗?这似乎很奇怪...
  5. 我应该将其实现Basket为流程管理器而不是简单的集合吗?

经过更多研究后,我得出以下结论:“传奇”是一种保留自己的事件并从外部监听事​​件的东西。基本上,它是一种聚合,也可以对自己的小世界之外发生的事件做出反应。

流程管理器处理来自外部的事件并发出命令。可以从共享相同标识符(如correlationId)的聚合事件中重建事件的历史记录。


似乎您正在尝试在系统中编码一个正式过程,以解释由一系列命令构成的现有非正式用例。这样做,看起来您正在创建除现有命令和事件之外的许多冗余命令和事件。那是你的意图吗?将事物形式化为代码流程的背后的业务需求是什么?您域中的哪些内容要求您确定此过程以及将其作为完整概念的原因?
guillaume31

这是一个完全组成的项目,目的是学习如何使两个相对独立的聚合一起工作。因此,实际上并没有真正的“业务需求”,并且我正在尝试尽可能避免这些命令和事件中的冗余。因此,与流程管理器的混乱,因为似乎不应重复聚合已处理的内容。但是,我需要以某种方式在这两个聚合之间保持联系。似乎使用因果关系和关联可能会有所帮助,但我需要尝试一下。
伊万·平塔尔

Answers:


14

回顾Rinat Abdullin关于不断发展的业务流程的文章。特别是,请注意他关于在快速变化的环境中开发业务流程的建议-流程经理“只是”自动替代了盯着屏幕的人。

我对流程管理器的思维模型是,它是事件源的投影,您可以查询未决命令的列表。

我是否需要坚持流程经理?看起来像我,但我不确定

这是一个读取模型。您可以在每次需要时从事件历史记录中重建流程管理器;或者您可以将其视为要更新的快照。

如果这样做,则需要为流程管理器保存事件。

否-流程经理是经理。它本身并没有做任何有用的事情。相反,它告诉汇总进行工作(即更改记录簿)。

我怎么知道ProductReserved事件用于什么篮子?也可以在这些标签上使用BasketId,还是泄漏信息

当然。 注意:在大多数“真实”购物域中,您不会在处理订单之前坚持保留库存;它给业务增加了不必要的争用。您的企业更有可能希望接受订单,然后在极少数情况下无法在要求的时间内完成订单表示歉意。

如何保持事件之间的关系,如何知道哪个ItemAdded产生了哪个ProductReserved事件?

消息具有元数据-特别是,您可以包括causationIdentifier(因此您可以识别哪些命令产生了哪些事件)和relatedIdentifier,以大体上跟踪对话。

例如,流程管理器在命令中写入自己的ID作为correlationId。复制该命令的相关性ID所产生的事件,然后您的流程管理器将订阅具有其相关性ID的所有事件。

我是否应该将Basket作为流程管理器而不是简单的汇总来实现?

我的建议是不。 但是 Udi Dahan有不同的意见,您应该进行审查;这就是说CQRS仅在您的聚集体是无味的情况下才有意义-Udi在流程管理器已成为主要拼写的地方使用了传奇。

流程经理应该检索集合体吗?

并不是的?流程管理者主要关注业务流程,而不是域状态。一个流程的实例将以其观察到的所有事件的历史记录的形式具有“状态”-响应事件Z的正确操作取决于我们是否看到了事件X和Y因此,您可能需要能够存储和加载该状态的表示形式(可以是平坦的状态,也可以是观察到的事件的历史记录)。

(我之所以说“不是真的”,是因为聚合定义不够明确,以至于声称观察到的事件列表 “聚合” 并不是完全错误的。区别更多的是语义,而不是实现-我们加载流程状态,然后确定要发送的消息发送到负责域状态的系统各部分。这里有些动摇。)

因此,PM不需要在另一种状态管理上使用一种状态管理,因为它仅负责现场直播,而从不在重放期间负责?

不完全是-状态管理不是执行者,而是执行者的跟踪者。在流程管理器不应该发出任何信号的情况下,可以给它与世界的惰性连接。换句话说,dispatch(command)是无操作。


1
您说:您可以在每次需要时从事件历史记录中重建流程管理器...但是要重建它,我需要为其保存事件。还是应该从汇总中的事件重建它?我苦苦挣扎的部分是:对于聚合,事件具有聚合ID,并且通过查找具有该聚合ID的所有事件可以很容易地进行重建。但是,我将如何为流程经理做到这一点?我应该为流程经理这样做吗?还是当流程管理者需要根据发生的事件来决定某项事情时,它应该查找聚合吗?
伊万·平塔尔

我所缺少的是事件源中的因果关系和相关性的概念。一旦我研究了这个问题,那么您对第四个问题的答案就说得通了。
伊万·平塔尔

1
我想回答@IvanPintar的第一个评论;流程经理应该检索集合体吗?他们是否应该根据处理的事件构建自己的事件?在这种情况下,事件处理程序将需要没有副作用,对吗?
杰夫(Jeff)

@Jeff在我为流程管理器做的一个示例中,它拥有自己的存储,并随每个已处理事件(一种读取模型)进行了更新。这很容易做到,而且很容易跟踪已经处理的内容。在另一个示例中,流程管理器根据聚合事件创建并存储了自己的事件。与上面类似,但是状态是事件源的。根据流程管理器保持的状态的复杂性,做一个或另一个可能会更容易。我发现第一种方法更简单。
伊万·平塔尔

有趣的是,只要流程管理器最终响应事件并调度命令,开发人员或多或少地取决于开发人员?
杰夫(Jeff)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.