Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/google-app-engine/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Domain driven design 如何加载聚合?_Domain Driven Design_Updates_Aggregateroot - Fatal编程技术网

Domain driven design 如何加载聚合?

Domain driven design 如何加载聚合?,domain-driven-design,updates,aggregateroot,Domain Driven Design,Updates,Aggregateroot,在有关ddd的文章/书籍中,在处理聚合的章节中,我感觉从未讨论过一个主题: 如何重新加载聚合 要么是太明显了,说也没用 或者这不是正确的做法 要么我不明白 如果我拿这些项目的订单为例,创建或更新我的不变量是一样的。 假设我必须更新一个已经存在订单的项目的价格,所以我将从重新加载聚合开始 但是我该怎么做呢 使用诸如automapper之类的工具来设置私有属性 是否使用一个构造函数,该构造函数接受根聚合、子存储库的id,并在此构造函数中分配私有属性 从头开始一项一项地重建一切,就像我正在创建

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

如何重新加载聚合

  • 要么是太明显了,说也没用
  • 或者这不是正确的做法
  • 要么我不明白
如果我拿这些项目的订单为例,创建或更新我的不变量是一样的。 假设我必须更新一个已经存在订单的项目的价格,所以我将从重新加载聚合开始

但是我该怎么做呢

  • 使用诸如automapper之类的工具来设置私有属性

  • 是否使用一个构造函数,该构造函数接受根聚合、子存储库的id,并在此构造函数中分配私有属性

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

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

    通常是这个

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


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

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

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

    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应该跟踪它加载的聚合,并知道是否有更改。如果有更改,它将以事务方式保存整个聚合,并进行乐观并发检查,这样,如果另一个进程更改了数据库中的聚合,提交将失败

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


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

    我不喜欢重用工厂的想法。在每一种非常重要的情况下,它们都会附带额外的验证(例如,请求的“名称”是否已经存在于数据库中?),这甚至可能会带来高昂的成本(webservice调用)。工厂正是为了第一次创建对象(实体),就是这样。当坚持补水时,我建议使用最简单的方法。如果它是一个框架,那没关系