限界上下文内部和外部的事件溯源

Posted

技术标签:

【中文标题】限界上下文内部和外部的事件溯源【英文标题】:EventSourcing inside and outside Bounded Contexts 【发布时间】:2017-06-10 20:08:10 【问题描述】:

我正在尝试使用 dddd 实现事件源系统。目前,我正在为我的事件如何以及在何处跨越有界上下文的边界而苦苦挣扎。

假设有两个有界上下文:

    产品管理 物流系统

产品管理拥有有关产品的所有知识。为简单起见,它只是“名称”。物流系统也有产品,但对它们的元数据一无所知。对他们来说,它主要是一个带有 ID 的物理盒子。但是当有人扫描这个产品时,他们也想显示名称。因此,ProductManagement BC 应该通知 Logistics BC,产品已注册并且名称已更改。因此,我将以 ProductManagement 中的事件结束,从 ProductAggregate 内部引发:

ProductManagement.Events.ProductRegistered
ProductManagement.Events.ProductNameChanged

当我得到正确的这些是我将保存到事件存储中的事件。这些也是将发布到消息总线中的事件。所以在物流方面,我会订阅这些活动。到目前为止一切顺利。

现在的问题是: 我将如何在物流方面处理此事件? Vaughn Vernon 在一次演讲中说,最好的做法是在应用层有一个事件处理程序,所以它基本上是一个应用程序服务。他还说,最好将其转换为一个或几个命令。我是否再次将所有收到的事件保存在物流端?我还要保存命令吗?如果出现问题,如何重现当前状态?或者我怎么知道,这不是接收限界上下文中处理的错误,而是错误的事件。如果我的转换命令被拒绝了怎么办?

我知道物流方面的聚合没有计算或更改。但我认为这对我的问题并不重要。

【问题讨论】:

【参考方案1】:

这里有几件事。

首先,您不必导入 Logistics BC 关于名称更改。您可以在需要时从客户那里从 PM BC 获取此信息。这通常由某种复合 UI 完成。 UI 组合可以在客户端或(Web)服务器上完成。您可能需要查看 Mauro Servienti 的文章 The secret of better UI composition,对此进行了描述。

但总的来说,这通常是这样的:

domain event -> pub/sub -> message consumer -> command -> domain command handler

所以,

    您将域事件从 PM BC 发布到总线 物流中有此事件的事件处理程序 事件处理程序可能会做一些检查,并将RegisterProduct 命令发送到同一个BC 命令照常处理,新的Product 聚合在物流中创建

它不仅在事件源系统中如此工作,而且在任何具有多个服务的系统中,使用事件驱动架构。

【讨论】:

【参考方案2】:

对于您描述的用例,您只需要物流系统使用的产品的一些属性。因此,物流系统可以通过订阅您描述的事件来保存它所需的产品信息的本地缓存——这可能是一个简单的内存缓存。当您处理读取模型时,它们不需要转换为命令或类似的东西,即一个看法。只需让一个简单的事件处理程序处理事件并在某处更新某些状态 - 无需在读取端对其进行事件源。当物流系统需要产品名称时,它只是从本地缓存中获取。由于产品管理仍然是事实的来源,因此您并没有破坏这两种上下文的自主性。

如果您需要重建状态,您只需擦除缓存并通过您的处理程序重播所有事件。但是请记住,产品管理上下文拥有这些事件,因此它们应该只保存在那里,而不是在物流上下文中 - 如果你想重建状态,你需要一种重新发布它们的方法

顺便说一下,这一系列博客文章描述了这个确切的用例:

https://www.tigerteam.dk/2014/micro-services-its-not-only-the-size-that-matters-its-also-how-you-use-them-part-1/

(如果我没记错的话在第 5 部分)

或者,您可以进行某种 UI 组合,其中名称取自产品管理上下文,其他详细信息取自物流上下文(也在上述博客文章中讨论)

【讨论】:

【参考方案3】:

[...] 他还说,最好将其转换为一个或多个命令。我是否再次将所有收到的事件保存在物流端?我还要保存命令吗?

首先,您必须询问领域专家该事件是否会导致影响 LS 上下文的副作用。只有在这种情况下,您必须订阅此事件并将相关命令发送到将更改并提交其状态的 LS 聚合,或者,如果您也选择事件源此聚合,则发送另一个事件。

如果出现问题,我如何重现当前状态?或者我怎么知道,这不是接收限界上下文中处理的错误,而是错误的事件?如果我的转换命令被拒绝了怎么办?

事件是发生的事情的表示,所以它不能是“错误的”。无论如何,由事件触发的命令可能会失败。你说的是哪种类型的失败?技术或特定领域?在第一种情况下,源事件将保留在总线中以供将来重试(可能在某些错误修复之后)。在第二种情况下,如果需要通知 PM 聚合结果,则 LS 聚合应该发出一个适当的事件,该事件反过来将由 PM 聚合处理。

【讨论】:

以上是关于限界上下文内部和外部的事件溯源的主要内容,如果未能解决你的问题,请参考以下文章

利用Spring Cloud实现微服务- 内部调用

CQRS / 事件溯源 / 事件总线 / 时序

Akka.NET 中的事件溯源和 CQRS

DDD 实战 :限界上下文映射和系统分层架构

限界上下文

领域驱动设计:领域子域和限界上下文