CQRS事件不包含更新读取模型所需的详细信息

Gol*_*den 17 cqrs

关于CQRS我有一件事没有得到:当引发的事件不包含更新读取模型所需的细节时,如何更新读取模型.

不幸的是,这是一种非常常见的情况.

示例:我将用户添加到组中,因此我发送addUserToGroup(userId,groupId)命令.收到此信息,由命令处理程序处理,创建,存储和发布userAddedToGroup事件.

现在,事件处理程序接收此事件和两个ID.现在应该有一个视图,列出所有用户都有他们所在的组的名称.要更新该视图的读取模型,我们需要用户ID(我们有)和组名(我们不知道)有,我们只有它的id).

所以问题是:我该如何处理这种情况?

目前,我想到了四种选择,都有其特定的缺点:

  1. 读取模型询问域.=>禁止,甚至不可能,因为域只有行为,没有(公共)状态.

  2. 读取模型从读取模型中的另一个表中读取组名称.=>工作,但如果没有匹配表怎么办?

  3. 将必要的数据添加到事件中.=>不起作用,因为这意味着我必须更新所有以前的事件,我无法预见有一天我可能需要哪些数据.

  4. 不要通过"常规"事件处理程序处理事件,而是在后台启动处理事件存储的ETL过程,创建必要的数据并写入读取的模型.=>工作,但对我来说,对于这样一个简单的场景来说,这似乎有点过多的开销.

所以,问题是:我如何正确处理这种情况?

Seb*_*ood 7

有两种常见的解决方案.

1)"事件充实"是指您确实将信息放在反映您提及的信息的事件上,例如组名.这样做可以在正确建模域名和欺骗之间进行.例如,如果您知道组名称发生更改,则在更改时发出名称并不是一个坏主意.想象一下,当您在报价或发票上创建订单项时,您希望在发票创建的事件中排出所售商品的价格.这是因为您必须尊重该价格,即使它稍后发生变化.

2)一次投射几个流.编写一台投影仪,观察各种流中的信息并将它们连接在一起.您可以观看用户和群组事件以及添加到群组活动的用户.根据系统中事件的顺序,您可能知道用户在知道组名称之前就在组中,但在开始之前您应该知道事件存储的常规属性.


Den*_*aub 5

事件不一定代表首先启动过程的命令的一对一映射.例如,如果您有一个命令:

SubmitPurchaseOrder
    Shopping Cart Id
    Shipping Address
    Billing Address
Run Code Online (Sandbox Code Playgroud)

生成的事件可能如下所示:

PurchaseOrderSubmitted
    Items (Id, Name, Amount, Price)
    Shipping Address
    Shipping Provider
    Our Shipping Cost
    Shipping Cost billed to Customer
    Billing Address
    VAT %
    VAT Amount
    First Time Customer
    ...
Run Code Online (Sandbox Code Playgroud)

通常,信息可用于域模型(通过命令提供或作为相关聚合的已知内部状态或作为处理的一部分计算).

此外,可以通过在处理期间查询读取模型或甚至不同的BC(例如,根据状态检索实际VAT%)来丰富事件.

你正确地假设事件可以(并且可能会)随时间而改变.如果您使用版本控制,这基本上无关紧要:添加新事件(例如SubmitPurchaseOrderV2)并向应该使用它的所有类添加适当的事件处理程序.无需更改旧事件,因为您不修改界面,所以仍然可以使用旧事件.这基本上归结为实践中开放/封闭原则的一个很好的例子.