如何处理(部分)依赖的聚合根?

dee*_* zg 5 domain-driven-design

我有Product.

Product有一些GeneralDetails,让我们说:sku, name, description。同时,会计师可以Product在某些ProductCalculations部分放置不同的值,例如purchasePrice, stockLevelExpenses, wholeSalesPrice, retailPrice.

所以,到目前为止,Product看起来像:

class Product{
   GeneralDetails Details;
   ProductCalculations Calculations;

   ChangeDetails(GeneralDetails details){}
   Recalculate(ProductCalculations calculations{}

}
Run Code Online (Sandbox Code Playgroud)

此设置将生成Product聚合根。但是现在,我想以一种方式拆分它,即产品经理可以输入/更新产品详细信息,但随后该会计师可以介入并独立地更改给定产品的计算,而不会出现并发问题。这将建议将其拆分为 2 个单独的聚合根。

但是,删除ProductDetails聚合也必须意味着删除ProductCalculations,它应该以事务性的方式发生。

假设它们是 2 个聚合根,这意味着它们有 2 个具有相应Delete方法的独立存储库,如何将其实现为原子事务?

我唯一能想到的是在ProductDetails被删除时引发事件,有一个处理程序(DomainService),它使用一些特殊的存储库来处理多个聚合根上的事务。

这种方法有什么问题和/或有更好的方法来处理它吗?

附注。ProductDetails删除时我不能允许最终一致性。

PS2。根据来自@乔恩的意见,Details以及Calculations创建和删除应在当的方式同步Details在创建/删除,Calculations也应创建/删除。另一方面,它们的更新应该是完全独立的。

小智 2

我认为您问题的答案在某种程度上取决于您使用的数据存储技术和数据存储模型,因为如果您可以将操作事务性推送到数据层,事情就会变得容易得多。

如果您使用面向文档的数据库(Cosmos DB、MongoDB 等),我会将您的Product聚合(包括DetailsCalculations)建模并存储为单个文档,并且您可以从以下位置免费获得原子事务和并发检查:数据库。

如果您必须将这些作为单独的文档/记录存储在数据存储中,那么提供原子事务和并发检查就成为您关注的问题。多年来,人们(尤其是使用实体框架的人)一直在使用工作单元模式来批处理多个存储库操作,并将它们作为单个操作提交到数据库(特定于 EF 的 UoW 实现)。Rob Conery在此建议,更好的选择是使用 Command 对象来封装需要作为单个事务执行的多部分操作。

无论如何,我鼓励您将此操作的管理保留在 内Product,以便消费者Product不知道保存期间发生了什么 - 他们只是高兴地调用product.SaveAsync(),并且不需要知道这是否会导致一条记录更新或十。只要Product注入完成工作所需的存储库,就不需要单独的域服务来协调此操作。Product监听其子级引发的事件并做出适当的响应并没有什么问题。

希望这可以帮助!