Domain driven design 使用来自多个聚合根的数据读取模型(不同上下文)

Domain driven design 使用来自多个聚合根的数据读取模型(不同上下文),domain-driven-design,cqrs,event-sourcing,Domain Driven Design,Cqrs,Event Sourcing,我很好奇如何将来自多个聚合根的数据连接到一个事件源聚合根的读取模型中。可以尝试举一个简单的例子: 如果我有一个名为Cart的聚合根,它在其事件流中支持以下事件(括号中的属性-请记住这是一个简单的示例): 当使用来自此事件流的数据投影读取模型时,这是正常的。例如,我可以投影一个cart对象,其外观如下: Cart(cartId: Int, products: List[Product]) Product(productId: Int, userLicenses: List[UserLicense

我很好奇如何将来自多个聚合根的数据连接到一个事件源聚合根的读取模型中。可以尝试举一个简单的例子:

如果我有一个名为Cart的聚合根,它在其事件流中支持以下事件(括号中的属性-请记住这是一个简单的示例):

当使用来自此事件流的数据投影读取模型时,这是正常的。例如,我可以投影一个cart对象,其外观如下:

Cart(cartId: Int, products: List[Product])

Product(productId: Int, userLicenses: List[UserLicense])
UserLicense(userId: Int)
但是,如何将另一个上下文中另一个聚合根中的数据连接到此购物车投影中呢。例如,如果我想使用来自另一个上下文中的产品聚合根的数据扩展读取模型。假设我想用productName和productType扩展它

考虑到我们在分布式系统中工作,产品和购物车将位于不同的服务/应用程序中

我认为一种解决方案是在命令和事件中包含数据。但是,如果有一个更大的读取模型,其中包含来自多个聚合根的数据,那么这似乎不能很好地扩展。此外,还必须能够核化和重建读取模型


我认为另一种解决方案是将数据从其他聚合根复制到其他应用程序/服务/上下文的存储中。例如,将productName和productType数据复制到购物车应用程序拥有的存储中,但不将其作为购物车事件流的一部分。然后,购物车应用程序必须侦听事件(例如ProductCreated、ProductNameChanged)以保持数据更新。我想这可能是一个可行的解决方案。

这个问题还涉及到事件驱动架构,而不仅仅是事件源。我认为您已经涵盖了从活动制作人处获取相关数据的大部分选项

另一种选择是,事件包含来自相关有界上下文的尽可能少的数据。至少是一个标识符。然而,在大多数情况下,一些数据应该进行非规范化以使其有意义。例如,将产品描述非规范化为
购物车
和最终的
订单
将非常有用,特别是当我做出选择后有人更改描述时。描述可能会从
蓝笔
更改为
红笔
,这将极大地改变我打算购买的内容。在这种情况下,
购物
BC中的
产品
可能由包含
Id
说明
的值对象表示


如果您现在想增加只读数据,我们只剩下从源BC检索它的选项。这可以使用一些API(Rest/ACL)在读取模型中完成,然后保存数据。为了使其更具容错性,可以选择消息传递/服务总线基础设施来处理额外数据的检索和相关读取模型记录的更新。

每个有界上下文应该松散耦合。我们在两个环境中遇到了类似的问题。我们找到的解决方案是通过在这些文件中创建上下文之间的所有通信来使用工作流。在其中,我们可以通过订阅事件处理程序来同步所需的模式。当我们使用Elixir时,我们使用的库被命令,它有自己的事件总线

但是在分布式系统中,您可以使用ApacheKafka。最后,我认为更简单的解决方案应该使您的模式尽可能干净(它还将帮助您尊重GDPR遵从性),并通过事件处理程序通过一个单独的层管理所有通信

要以“真实”的方式查看此解决方案,我可以向您推荐一个使用Elixir构建的优秀示例存储库

Cart(cartId: Int, products: List[Product])

Product(productId: Int, userLicenses: List[UserLicense])
UserLicense(userId: Int)