Domain driven design 如何加载聚合?
在有关ddd的文章/书籍中,在处理聚合的章节中,我感觉从未讨论过一个主题: 如何重新加载聚合Domain driven design 如何加载聚合?,domain-driven-design,updates,aggregateroot,Domain Driven Design,Updates,Aggregateroot,在有关ddd的文章/书籍中,在处理聚合的章节中,我感觉从未讨论过一个主题: 如何重新加载聚合 要么是太明显了,说也没用 或者这不是正确的做法 要么我不明白 如果我拿这些项目的订单为例,创建或更新我的不变量是一样的。 假设我必须更新一个已经存在订单的项目的价格,所以我将从重新加载聚合开始 但是我该怎么做呢 使用诸如automapper之类的工具来设置私有属性 是否使用一个构造函数,该构造函数接受根聚合、子存储库的id,并在此构造函数中分配私有属性 从头开始一项一项地重建一切,就像我正在创建
- 要么是太明显了,说也没用
- 或者这不是正确的做法
- 要么我不明白
通常,您的设计会包含一些工厂模式的表现形式,该模式从其他信息创建聚合-您通常可以在存储库的外观后面使用类似的工厂,以在重新加载时获得相同的结果。您询问要重新加载聚合的系统,但这个系统也应该能够创建和更新聚合。创建它们时,您希望避免重复。在更新它们时,您希望以事务方式保存整个聚合,并希望避免并发更新在数据库中留下不一致的状态。设想两个单独的线程分别为同一个产品添加一个行项目,您不允许重复产品。每个线程上的顺序都是一致的,但是如果您没有为此做好准备,那么在关系数据库中,您最终可能会插入重复的行项目 您还希望将核心代码与数据访问代码隔离开来。为此,我通常使用以下界面:
public interface IOrderUnitOfWork
{
Task<Order> GetOrCreate(Guid id, Func<Task<Order>> createFunc);
Task<Order> Get(Guid id);
Task Commit();
}
公共接口IOrderUnitOfWork
{
任务GetOrCreate(Guid id,Func createFunc);
任务获取(Guid id);
任务提交();
}
这项工作如下:
GetOrCreate
。这样做的原因是使运算幂等。第一次调用该操作时,它将调用createFunc
。如果重试该操作,它将找到订单,并且不会再次创建该订单
Get
。如果订单不存在,这将引发NotFoundException。这使您的代码更干净,在您的用例中节省了数百个空检查
Commit
。UnitOfWork应该跟踪它加载的聚合,并知道是否有更改。如果有更改,它将以事务方式保存整个聚合,并进行乐观并发检查,这样,如果另一个进程更改了数据库中的聚合,提交将失败
您也可以考虑事件源代码,因此您可以将聚合状态存储为一系列事件,这些事件顺序地构建了聚合的当前状态(OrthRead、RealItMead、LayItEdMead、LayItMechange等)。在这种情况下,工作单元的实现将加载事件,并且您的聚合将具有某种ApplyEvents方法来构造当前状态。
我不喜欢重用工厂的想法。在每一种非常重要的情况下,它们都会附带额外的验证(例如,请求的“名称”是否已经存在于数据库中?),这甚至可能会带来高昂的成本(webservice调用)。工厂正是为了第一次创建对象(实体),就是这样。当坚持补水时,我建议使用最简单的方法。如果它是一个框架,那没关系