我很好奇如何在事件源聚合根的读取模型中从多个聚合根连接数据。可以尝试举一个简单的例子:
如果我有一个称为Cart的聚合根,它支持事件流中的以下事件(括号中的属性-请记住,这是一个简单的示例):
AddProductToCart(cartId: Int, productId: Int)
RemoveProductFromCart(cartId: Int, productId: Int)
AddUserLicenseToProduct(cartId: Int, productId: Int, userId: Int)
RemoveUserLicenseFromProduct(cartId: Int, productId: Int, userId: Int)
EmptyCart(cartId: Int)
使用来自此事件流的数据投影读取模型时可以。例如,我可以投影一个看起来像这样的购物车对象:
Cart(cartId: Int, products: List[Product])
Product(productId: Int, userLicenses: List[UserLicense])
UserLicense(userId: Int)
但是,一个人如何将来自另一个上下文中的另一个聚合根的数据连接到此购物车投影中。例如,如果我想使用产品聚合根中的数据扩展读取模型,该数据位于另一个上下文中。假设我想用productName和productType扩展它。
考虑到我们在分布式系统中工作,产品和购物车将驻留在不同的服务/应用程序中。
我想一种解决方案是将数据包含在命令和事件中。但是,如果使用来自多个聚合根的数据来使用更大的读取模型,则似乎无法很好地扩展。另外,还必须能够核对并重建读取的模型。
我想另一种解决方案是将来自其他聚合根的数据复制到其他应用程序/服务/上下文的存储中。例如,将productName和productType数据复制到Cart应用程序拥有的存储中,但不将其作为Cart事件流的一部分。然后,购物车应用程序将不得不侦听事件(例如ProductCreated,ProductNameChanged)以保持数据更新。我想这可能是一个可行的解决方案。
答案 0 :(得分:1)
每个有界上下文应松散耦合。我们在两个环境中也遇到了类似的问题。我们发现的解决方案是通过在那些文件的上下文之间创建所有通信来使用工作流。在其中我们可以通过订阅事件处理程序来同步所需的架构。在使用Elixir时,我们使用的库是Commanded,它具有自己的事件总线。
但是在分布式系统中,您可以使用Apache Kafka。归根结底,我认为更简单的解决方案应使您的架构尽可能保持最干净(它还可以帮助您遵守GDPR要求),并由事件处理程序通过单独的层来管理所有通信。>
要以“现实”的方式查看此解决方案,我建议您使用Elixir构建一个出色的示例存储库。
答案 1 :(得分:0)
这个问题还涉及事件驱动的体系结构,而不仅仅是事件源。我认为您已经涵盖了从事件制作者那里获取相关数据的大多数选项。
另一种选择是,事件包含来自相关有界上下文的尽可能少的数据。至少是一个标识符。但是,在大多数情况下,应对某些数据进行规范化以使其有意义。例如,将产品描述规范化为Cart
并最终归为Order
会很有帮助,尤其是当有人在我做出选择后更改了描述时。说明可能会从Blue pen
变为Red pen
,这将大大改变我打算购买的商品。在这种情况下,您的Product
BC {em>中的Shopping
可能由包含Id
和Description
的值对象表示。
如果您现在想扩充只读数据,我们只能选择从源BC检索数据的选项。可以使用某些API(Rest / ACL)在读取模型中完成此操作,然后保存数据。为了使其更具容错性,可以选择消息传递/服务总线基础结构来处理其他数据的检索和相关读取模型记录的更新。