什么是聚合根?

Din*_*nah 417 design-patterns ddd-repositories aggregateroot repository-pattern

我试图了解如何正确使用存储库模式.Aggregate Root的核心概念不断涌现.在搜索Web和Stack Overflow以获取有关聚合根的帮助时,我会不断发现有关它们的讨论以及指向应该包含基本定义的页面的死链接.

在存储库模式的上下文中,什么是聚合根?

Jef*_*nal 290

在存储库模式的上下文中,聚合根是客户端代码从存储库加载的唯一对象.

存储库封装了对子对象的访问 - 从调用者的角度来看,它会自动加载它们,无论是在加载根目录还是实际需要它们时(如延迟加载).

例如,您可能有一个Order对多个LineItem对象封装操作的对象.您的客户端代码永远不会LineItem直接加载对象,只Order包含它们,这将是您域的该部分的聚合根.

  • 假设,如果客户端代码需要LineItem用于其他目的,那么它是否会形成一个单独的聚合(假设其他对象涉及与Order对象无关)? (18认同)
  • @Ahmad,其他聚合可能将LineItems称为只读数据,它们不能*改变它们.如果其他聚合可以更改它们,则无法保护订单的不变量(也不能保护订单项). (18认同)
  • @Neil:我会使用任何可用的语言机制强制执行它 - 例如,通过创建一个表示数据的不可变类. (4认同)
  • 看看这个,例如http://www.lostechies.com/blogs/jimmy_bogard/archive/2010/02/23/strengthening-your-domain-aggregate-construction.aspx.在示例中,Customer是Order的不变量,对吧?但是,Customer也可以是另一个聚合根?或者我在这里错过了一些基本的理解? (3认同)
  • @Jeff你说"他们只是无法改变它们" - 这是可以强制执行的,还是一个常规问题? (3认同)
  • @Neil,我不知道任何语言让这么容易.只是创建另一个类来表示它应该是只读的上下文中的数据要容易得多.这些有时被称为[有界上下文](http://www.markhneedham.com/blog/2009/03/07/ddd-bounded-contexts/). (3认同)
  • 在该示例中,Customer和Order可能都是聚合根,但我不认为作者建议您可以通过Customer对象(通过某些方法,如Customer.RemoveFirstItemFromOpenOrders())或反向(例如Order)更改Orders. UpdateCustomerBillingAddress()). (2认同)
  • @Jeff我的意思是 - 你如何创建一个可以作为聚合的一部分的类(因此在聚合中被其他类修改)但不是由不同聚合中的类修改?如果它是不可变的,你就无法从任何地方改变它. (2认同)
  • 这个答案在存储库模式的上下文中是正确的,但经典 DDD 的定义更全面一些;请参阅下面@Jason的回答 (2认同)
  • @ NeilBarnwell,@ .JeffSternal:获得"相同"对象的可变和不可变版本的一种方法是拥有一个只读接口(具有只读方法的基类)和一个可变的具体类型(具有两者的派生类)读容量和写容量).有些语言通过提供原生支持来区分这两种语言(例如C++编译器强制只在`const`对象上调用`const`方法)使这更简单. (2认同)

jas*_*son 195

来自埃文斯DDD:

AGGREGATE是一组关联对象,我们将其视为数据更改的单位.每个AGGREGATE都有一个根和一个边界.边界定义了AGGREGATE中的内容.根是AGGREGATE中包含的单个特定ENTITY.

和:

根是AGGREGATE的唯一成员,允许外部对象保存对[.]的引用.

这意味着聚合根是唯一可以从存储库加载的对象.

一个例子是包含Customer实体和Address实体的模型.我们永远不会Address直接从模型访问实体,因为没有关联的上下文就没有意义Customer.因此,我们可以说,CustomerAddress在一起形成聚集体,并且Customer是一个聚合根.

  • [来自Eric Evans的更新](http://gojko.net/2009/03/12/qcon-london-2009-eric-evans-what-ive-learned-about-ddd-since-the-book/):强调聚合根是事务/并发的一致性边界,并且强调外部实体不能保存对其他聚合的子实体的引用. (53认同)
  • 所以这个措辞永远让我感到困惑.`每个AGGREGATE都有一个root`和`root是AGGREGATE`的唯一*成员* - 这个verbage意味着root是Aggregate的属性.但在所有示例中,反过来说:root包含聚合属性.你能澄清一下吗? (3认同)
  • 一般来说,在客户订单行项目范例中,客户将是聚合根。客户的实例将是该聚合根的实例。当谈到称为“客户”的聚合根时,您正在讨论构成客户实例的“客户”的逻辑构造。客户的集合只是一个集合。 (2认同)

Mar*_*zak 93

聚合根是简单想法的复杂名称.


大概的概念

精心设计的类图封装了它的内部结构.调用访问此结构的点aggregate root.

在此输入图像描述

您的解决方案的内部可能非常复杂,但此层次结构的用户将使用root.doSomethingWhichHasBusinessMeaning().


检查这个简单的类层次结构 在此输入图像描述

你想怎么骑车?选择更好的api

选项A(它只是以某种方式工作):

car.ride();
Run Code Online (Sandbox Code Playgroud)

选项B(用户可以访问类inernals):

if(car.getTires().getUsageLevel()< Car.ACCEPTABLE_TIRE_USAGE)
    for (Wheel w: car:getWheels()){
        w.spin();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您认为选项A更好,那么祝贺.你得到了主要原因aggregate root.


聚合根封装了多个类.您只能通过主对象操纵整个层次结构.

  • 我喜欢这个例子,但我很难找到一个客户应该引用Engine的场景.似乎Engine应该封装在Car后面.你能详细说明一下吗? (14认同)
  • @ParamaDharmika 当然,你可以这样建模。这取决于您的客户对汽车的“先进”程度。在基本模型中,他应该有权访问“car”聚合根。您也可以允许出现如图所示的情况。正确的解决方案取决于应用程序的业务模型。每种情况可能有所不同。 (4认同)

Fra*_*ino 34

想象一下,你有一个计算机实体,这个实体也离不开它的软件实体和硬件实体.这些形成了Computer聚合,即域的计算机部分的迷你生态系统.

Aggregate Root是聚合中的母舰实体(在我们的例子中Computer),通常的做法是让您的存储库仅与作为聚合根的实体一起工作,并且该实体负责初始化其他实体.

将聚合根视为聚合的入口点.

在C#代码中:

public class Computer : IEntity, IAggregateRoot
{
    public Hardware Hardware { get; set; }
    public Software Software { get; set; }
}

public class Hardware : IEntity { }
public class Software : IValueObject { }

public class Repository<T> : IRepository<T> where T : IAggregateRoot {}
Run Code Online (Sandbox Code Playgroud)

请记住,硬件可能也是一个ValueObject(没有自己的身份),仅作为示例.

  • `哪里T:IAggregateRoot` - 这一天是我的一天 (5认同)
  • 我认为这个措辞有点矛盾,这就是我在尝试学习这一点时感到困惑的地方。您说计算机是聚合,但是您说根将是聚合内的母舰实体。那么本例中聚合内部的“母舰”实体是哪一个呢? (2认同)
  • 来自未来的问候!这家伙的意思是,计算机本身是聚合根,而计算机及其内部的所有内容都是聚合。或者更清楚地说:机箱本身是聚合根,而整个计算机是聚合(构成“计算机的所有内容的集合,例如 RGB 照明、硬件、电源、操作系统等)”。 (2认同)
  • IAggregateRoot 技术出现在 Microsoft 的文档中:https://learn.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/infrastruct-persistence-layer-design (2认同)

Cap*_*chi 14

如果您遵循数据库优先方法,则聚合根通常是1-many关系的1侧的表.

最常见的例子是人.每个人都有许多地址,一张或多张工资单,发票,CRM条目等.情况并非总是如此,但却是9/10倍.

我们目前正在开发一个电子商务平台,我们基本上有两个聚合根:

  1. 顾客
  2. 卖家

客户提供联系信息,我们为他们分配交易,交易获取订单项等.

卖家销售产品,联系人,关于我们页面,特别优惠等.

这些由客户和卖方存储库分别处理.

  • 如果您遵循数据库优先方法,那么您就没有在实践域驱动设计,而是在遵循数据驱动设计。 (6认同)
  • 这是一个问答论坛,人们来解决问题和/或学习 - 这不是我在戳你.根据定义,DDD是一种思维模式,对许多人而言是令人困惑的,所以这就是我确保评论是为那些正在学习DDD的人做出的,以帮助减轻设计方法的任何潜在混淆. (5认同)

Otá*_*cio 8

来自断开的链接:

在Aggregate中有一个Aggregate Root.聚合根是聚合中所有其他实体和值对象的父实体.

存储库在聚合根上运行.

更多信息也可以在这里找到.

  • 谢谢.这绝对是我不断遇到的最常见,最令人沮丧的断链. (4认同)

Ibr*_*luf 8

黛娜:

在存储库的上下文中,聚合根是没有父实体的实体.它包含零个,一个或多个子实体,其存在依赖于父对象的身份.这是存储库中的一对多关系.这些儿童实体是普通的聚合体.

在此输入图像描述

  • @JorgeeFG 真正的答案是根本没有人有任何线索。周围散布着太多相互矛盾的信息。 (2认同)
  • 子实体不是聚合,它们只是恰好是聚合根控制的聚合成员的实体。“集合”是实体的“逻辑”分组。 (2认同)

小智 6

聚集意味着收集某物。
就像树的顶部节点,从那里我们可以访问<html>网页文档中的所有节点。
博客类推,一个用户可以有很多帖子,每个帖子可以有很多评论。因此,如果我们获取任何用户,则它可以充当root用户访问所有相关帖子以及这些帖子的进一步评论。这些统称为收集或汇总