【问题标题】:How to load an aggregate?如何加载聚合?
【发布时间】:2021-01-27 04:50:25
【问题描述】:

在处理聚合的章节中关于 ddd 的文章/书籍中,我感觉从未讨论过一个主题:

如何重新加载聚合?

  • 要么太明显了,说起来也没用
  • 要么这样做是不对的
  • 要么我不明白

如果我以创建或更新这些项目的订单为例,我的不变量是相同的。 假设我必须更新现有订单的商品价格,所以我将从重新加载我的聚合开始。

但是我该怎么做呢?

  1. 使用诸如 automapper 之类的工具可以设置私有属性?

  2. 使用一个构造函数,该构造函数获取根聚合、子存储库的 id 并在此构造函数中分配私有属性?

  3. 从头开始逐项重建所有内容,就好像我正在创建一个新的一样?

【问题讨论】:

    标签: domain-driven-design updates aggregateroot


    【解决方案1】:

    从头开始逐项重建一切,就好像我正在创建一个新的一样?

    通常是这个。

    使用适当的通用机制从持久存储中加载数据:字节数组、json 文档、记录集等等。现在您已将信息保存在内存中,将这些信息复制到生成域模型对象的工厂中。

    通常,您的设计将包含工厂模式的一些表现形式,该模式从其他信息创建聚合 - 您通常可以在存储库的外观后面使用类似的工厂来在重新加载时获得相同的结果。

    【讨论】:

    • 我不喜欢重用工厂的想法。在每一个重要的情况下,它们都带有额外的验证(例如,请求的“名称”是否已经存在于数据库中?)这甚至可能是昂贵的(网络服务调用)。工厂正是为此:对象(实体)第一次创建,仅此而已。从持久性中补充水分时,我建议使用最简单的方法。如果它是一个框架,那没关系
    【解决方案2】:

    您询问有关重新加载聚合的系统,但该系统也应该能够创建和更新聚合。创建它们时,您要避免重复。更新它们时,您希望以事务方式保存整个聚合,并且希望避免并发更新在数据库中留下不一致的状态。考虑两个单独的线程,每个线程都为同一产品添加一个行项目,并且您不允许重复的产品。每个线程的顺序是一致的,但如果您不为此做好准备,在关系数据库中,您最终可能会插入重复的行项目。

    您还希望将核心代码与数据访问代码隔离开来。为此,我通常使用以下接口:

    public interface IOrderUnitOfWork
    {
        Task<Order> GetOrCreate(Guid id, Func<Task<Order>> createFunc);
        Task<Order> Get(Guid id);
        Task Commit();
    }
    

    它的工作原理如下:

    1. 对于需要创建聚合的用例,我使用GetOrCreate。这样做的原因是使操作具有幂等性。第一次调用该操作时,它将调用createFunc。如果您重试该操作,它会找到该订单,并且不会再次创建它。

    2. 对于应该已经存在聚合的用例(添加行项目,没有订单就无法工作)我使用Get。如果 Order 不存在,这将抛出 NotFoundException。这使您的代码更简洁,在您的用例中节省了数百个空检查。

    3. 在每个用例结束时,我都会调用Commit。 UnitOfWork 应该跟踪它加载的聚合,并知道是否有更改。如果发生更改,它会以事务方式和乐观并发检查保存整个聚合,因此如果另一个进程更改了数据库中的聚合,提交将失败。

    现在您可以针对此接口实现您的用例,而不管您使用何种技术来实现它。我使用了 ORM(实体框架)和基于 json 的数据存储。将聚合存储为文档的优点是默认情况下它是事务性/原子性的。如果您使用带有 ORM 的关系数据库,则可以使 ORM 将您的聚合直接映射到数据库,因此您不必在数据访问层中创建所有类的副本,然后使用 Automapper 或手动映射您的 DAO对象添加到您的核心对象中,因为这意味着大量的代码不会增加任何价值。

    您也可以考虑事件溯源,因此您可以将聚合状态存储为一系列事件,这些事件按顺序应用构建聚合的当前状态(OrderCreated、LineItemAdded、LineItemAdded、LineItemChanged 等)。在这种情况下,工作单元的实现将加载事件,并且您的聚合将具有某种 ApplyEvents 方法来构造当前状态。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多