Domain driven design 聚合根引用其他聚合根

Domain driven design 聚合根引用其他聚合根,domain-driven-design,repository,aggregate,loading,aggregateroot,Domain Driven Design,Repository,Aggregate,Loading,Aggregateroot,我目前正在大量使用DDD,当从其他聚合根加载/操作聚合根时,我面临一个问题 对于模型中的每个聚合根,我还有一个存储库。存储库负责处理根目录的持久性操作 假设我有两个聚合根,其中有一些成员(实体和值对象) AggregateRoot1和AggregateRoot2 AggregateRoot1有一个引用AggregateRoot2的实体成员 加载AggregateRoot1时,是否也应加载AggregateRoot2 AggregateRoot2的存储库是否应该对此负责 如果是,Aggregate

我目前正在大量使用DDD,当从其他聚合根加载/操作聚合根时,我面临一个问题

对于模型中的每个聚合根,我还有一个存储库。存储库负责处理根目录的持久性操作

假设我有两个聚合根,其中有一些成员(实体和值对象)

AggregateRoot1和AggregateRoot2

AggregateRoot1有一个引用AggregateRoot2的实体成员

  • 加载AggregateRoot1时,是否也应加载AggregateRoot2
  • AggregateRoot2的存储库是否应该对此负责
  • 如果是,AggregateRoot1中的实体可以调用AggregateRoot2的存储库进行加载吗
  • 另外,当我在AggregateRoot1中的实体与AggregateRoot2之间创建关联时,应该通过实体还是通过AggregateRoot2的存储库来完成

    希望我的问题有意义

    [编辑]

    当前解决方案

    在他的帮助下,我想出了以下解决方案:

    如问题中所述,聚合根可以具有引用其他根的子级。将root2分配给root1的一个成员时,root1的存储库将负责检测此更改,并将其委托给root2的存储库

    public void SomeMethod()
    {
        AggregateRoot1 root1 = AggregateRoot1Repository.GetById("someIdentification");
        root1.EntityMember1.AggregateRoot2 = new AggregateRoot2();
        AggregateRoot1Repository.Update(root1);
    }
    
    public class AggregateRoot1Repository
    {
        public static void Update(AggregateRoot1 root1)
        {
            //Implement some mechanism to detect changes to referenced roots
            AggregateRoot2Repository.HandleReference(root1.EntityMember1, root1.EntityMember1.AggregateRoot2)
        }
    }
    
    这只是一个简单的例子,不包括德米特法或其他最佳原则/实践:-)


    欢迎进一步评论。

    也许AggregateRoot1存储库在构建AggregateRoot1实体时可以调用AggregateRoot2存储库


    我不认为这会使ddd失效,因为存储库仍然负责获取/创建它们自己的实体。

    我自己也经历过这种情况,并得出结论,让子聚合以优雅的方式工作太令人头疼了。相反,我会考虑你是否真的需要把第二个聚合作为第一个孩子的引用。如果只保留聚合ID的引用,而不是实际聚合本身,则会使工作更轻松。然后,如果存在涉及这两个聚合的域逻辑,则可以将其提取到域服务,如下所示:

    public class DomainService
    {
        private readonly IAggregate1Repository _aggregate1Repository;
        private readonly IAggregate2Repository _aggregate2Repository;
    
        public void DoSomething(Guid aggregateID)
        {
            Aggregate1 agg1 = _aggregate1Repository.Get(aggregateID);
            Aggregate2 agg2 = _aggregate2Repository.Get(agg1.Aggregate2ID);
    
            agg1.DoSomething(agg2);
        }
    }
    
    编辑:


    我真的推荐这些关于这个主题的文章:

    这种方法有一些问题。首先,您应该为每个聚合及其完成设置一个存储库。一个存储库调用另一个存储库违反了这一规则。其次,关于聚合关系的一个良好实践是,一个根聚合应该通过其id与另一个根聚合进行通信,而不是使用其引用。这样做,可以使每个聚合独立于另一个聚合。在根聚合中只保留对组成相同聚合的类的引用。

    我也考虑过这一点,但是如果Aggregate2引用了Aggregate3,Aggregate3引用了另一个,那么又会怎样呢。这可能是一个相当大的对象图。这些场景的建议策略是什么?是的,我知道您不必太担心DDD中的实现,但在这一点上,如果我认为图太大,我会懒散地加载聚合。好的,到目前为止还不错。:-)但是,当我在AggregateRoot1.Entity1-->AggregateRoot2之间创建关系时,这应该通过AggregateRootRepository1.AddRoot2ToEntity1(root1,root2)还是通过AggregateRepository2.AddRoot2ToRoot1(root1,root2)或更直接的赋值来实现,我个人认为没有。我会通过以下方式分配它:“entity1.Entity2=Entity2”,然后存储库应该能够检测到这种关系,并做它需要做的事情(即,更新数据库列,如果它是底层存储),谢谢TWith2Sugars,但我真的希望在结束这个问题之前有更多的反馈。我个人很欣赏你的回答,我可以看到目前的方法越来越混乱,我认为DavidMaster84的解决方案更为优雅。Ie将引用作为id保留,并将这种类型的域逻辑提取到域服务中。我可以这样说,因为我最初尝试以同样的方式实现这个问题,我发现自己陷入了一个混乱:)你可能也想在这里阅读关于类似问题的建议:我听到了,但那里的存储库不是用来管理聚合根,并怀着某种善意,管理根之间的关系的吗。和域服务来处理不适合单个实体的行为?在我看来,在阅读域服务的定义时,让域服务负责处理根之间的引用是错误的。。。我可能是错的,所以如果能提供更多的支持,我们将不胜感激,谢谢;它可以调用涉及多个聚合的功能,即“自然不适合单个实体的行为”。在大多数模型中,所有聚合都以某种形式以关系数据库的形式相互关联,聚合的目的是将依赖关系图分解为可管理的组。如果你在模型中保持所有聚合之间的关系,那么它将违背聚合的观点;您的问题是关于在聚合根之间运行操作,我认为这是域服务适合的地方。我想很快你就会问自己兔子洞有多深…+1。这也是沃恩·弗农(Vaughn Vernon)在其“根据身份引用其他集合”规则中所主张的。其优点包括总体聚合大小可以提高性能和简化分区。我想它的缺点是有时会降低模型代码的表达能力,因此它是一个t