什么是汇总根?


447

我正在设法弄清楚如何正确使用存储库模式。聚合根的中心概念不断出现。在网络和Stack Overflow上搜索有关总根的帮助时,我一直在寻找有关它们的讨论以及指向应该包含基本定义的页面的无效链接。

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


16
考虑审查以下案例研究。有效的集合设计第一部分:建模单个集合dddcommunity.org/wp-content/uploads/files/pdf_articles/…第二部分:使集合协同工作dddcommunity.org/wp-content/uploads/files/pdf_articles/…第三部分:通过发现获得洞察力dddcommunity.org/wp-content/uploads/files/pdf_articles/…–
Ben Vitale,

Answers:


310

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

存储库封装了对子对象的访问权限-从调用者的角度来看,它会在加载根的同时或实际需要它们时(与延迟加载一样)自动加载它们。

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


21
假设,如果客户代码出于某些其他目的需要LineItem,是否会形成单独的聚合(假设将涉及其他与Order对象无关的对象)?
艾哈迈德(Ahmad)2010年

20
@Ahmad,其他聚合可能将LineItems称为只读数据,但无法更改它们。如果其他汇总可以更改它们,则您将无法保护订单的不变量(也不包括订单项)。
Jeff Sternal

4
看看这个,例如lostechies.com/blogs/jimmy_bogard/archive/2010/02/23/…。在示例中,客户是订单的不变式,对吗?但是,客户还可以是另一个聚合根吗?还是我在这里缺少一些基本的了解?
艾哈迈德(Ahmad)2010年

3
@Jeff您说过“他们无法更改它们”-是可执行的还是约定俗成的?
尼尔·巴恩威尔

4
@Neil:我会使用任何可用的语言机制来强制执行它,例如,通过创建一个不可变的类来表示数据。
Jeff Sternal

206

从Evans DDD:

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

和:

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

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

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


57
Eric Evans的更新:强调聚合根是事务/并发的一致性边界,并强调外部实体不能持有对其他聚合子实体的引用。
Brian Low

3
因此,这些混乱永远使我困惑。Each AGGREGATE has a rootThe root is the only *member* of the AGGREGATE-这verbage意味着根是在聚集属性。但是在所有示例中,都是相反的:根包含聚合的属性。你能澄清一下吗?
Sinaesthetic,2015年

1
只是为了使我的语言正确,Customer该类是否被视为聚合根或Customer 实例

1
一般来说,在“客户订单行项目”范式中,“客户”将是“汇总根”。客户实例将是该聚合根的实例。当谈到称为客户的聚合根时,您正在讨论组成客户实例的客户的逻辑构造。客户集合只是一个集合。
Ibrahim Malluf '16

111

聚合根是简单概念的复杂名称。


大概的概念

设计良好的类图封装了其内部。您访问此结构所经过的点称为aggregate root

在此处输入图片说明

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


检查这个简单的类层次结构 在此处输入图片说明

您想如何开车?选择更好的API

选项A(以某种方式起作用):

car.ride();

选项B(用户可以访问班级内部信息):

if(car.getTires().getUsageLevel()< Car.ACCEPTABLE_TIRE_USAGE)
    for (Wheel w: car:getWheels()){
        w.spin();
    }
}

如果您认为选项A更好,那么恭喜您。你得到背后的主要原因aggregate root


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


17
我喜欢该示例,但正在努力寻找一个客户应参考Engine的方案。看来Engine应该封装在Car后面。您能详细说明一下吗?
emragins

在我看来,发动机本身必须在特定于汽车的模型中,例如具有3000cc发动机的BMW 5系。通过这种建模,发动机是汽车的组成部分。
Parama Dharmika

1
@ParamaDharmika当然可以,您可以通过这种方式进行建模。这取决于您的客户对汽车的“先进程度”。在基本模型中,他应该有权访问car聚合根。您也可以允许类似图纸上的情况。正确的解决方案取决于应用程序的业务模型。每种情况下可能有所不同。
Marcin Szymczak

1
@MarcinSzymczak正确,无法完全同意解决方案取决于领域模型本身
Parama Dharmika

实际上,车轮是包含轮胎(和其他零件)的集合体。如果您的规则要求只能通过Car Root-Aggregate访问Wheel聚合,则引擎也包含在Car Root Aggregate中,并且不应在Car之外访问。那就是Car实例的境界。除非是他/她的车,否则车主(客户)将不会引用引擎。
Ibrahim Malluf

35

假设您有一个计算机实体,没有该软件实体和硬件实体,该实体也无法生存。这些构成Computer域的计算机部分的总体,小型生态系统。

聚合根是聚合内的母公司实体(在我们的示例中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 {}

请记住,硬件也可能也是ValueObject(本身没有标识),仅将其视为示例。


6
where T : IAggregateRoot-这个让我很快乐
克里斯蒂安·E.

我认为措辞有点矛盾,这就是使我感到困惑的原因。您说的是计算机是聚合的,但是您的意思是根将是聚合内部的母公司实体。那么,在此示例中,集合内的“母系”实体是哪个?
Sinaesthetic

来自未来的问候!那家伙的意思是计算机本身就是聚合根,而计算机及其内部的所有东西都是聚合根。或者更明确:通过自身的情况是聚合根,而整个计算机是聚合(一切的集合,构成了“计算机,如RGB照明,五金,电源供应器,OS等)。
船长剑八

该IAggregateRoot技术显示了微软的文档中:docs.microsoft.com/en-us/dotnet/architecture/microservices/...
塞缪尔·丹尼尔森

16

如果遵循数据库优先方法,则聚合根通常是一对多关系的1侧的表。

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

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

  1. 顾客
  2. 卖家

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

卖家出售产品,与他人联系,关于我们的页面,特价优惠等。

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


8
如果您遵循数据库优先方法,那么您就没有在实践域驱动设计,而是在遵循数据驱动设计。
Sinaesthetic

5
这是一个问答论坛,人们来这里解决问题和/或学习-那不是我在嘲笑你。根据定义,DDD的思想观念比其他任何事物都重要,它使许多人感到困惑,因此,我要确保对正在学习DDD的人们进行评论,以帮助减轻设计方法学的任何潜在混淆。
Sinaesthetic

12

黛娜:

在存储库的上下文中,聚合根是没有父实体的实体。它包含零个,一个或多个子实体,其存在取决于父实体的身份。这是存储库中的一对多关系。那些子实体是简单的集合。

在此处输入图片说明


1
因此,如果您是汽车销售商,那么汽车本身就是一个聚合根吗?因为您可能有很多没有客户的汽车
JorgeeFG

2
@JorgeeFG真正的答案是没有人有任何线索。周围有太多冲突的信息。
Mardoxx

3
子实体不是聚合,它们只是碰巧是聚合根控制的聚合成员的实体。“集合”是实体的逻辑分组。
Sinaesthetic

@JorgeeFG实际上取决于您正在设计的有限上下文。如果您是汽车销售商,那么类似汽车商店的东西将成为总根,而在汽车下面则是汽车...
jokab

8

断开的链接

聚合中有一个聚合根。聚合根是聚合内所有其他实体和值对象的父实体。

存储库在聚合根上运行。

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


4
谢谢。这绝对是我不断遇到的最常见和令人沮丧的断开链接。
迪纳

另外,措辞似乎是倒退的。根如何聚集物中同时成为父对象?
Sinaesthetic,2015年

1
聚合根是根类。普通聚合始终包含在聚合根中。使用上面提供的图...客户就是聚合根。客户可以拥有一辆或多辆汽车。汽车是与客户有关的集合体。汽车有引擎。引擎是包含在汽车集合中的集合。使客户成为总根的原因是模型的假设,即始终通过拥有汽车的客户来访问汽车或其组件。
Ibrahim Malluf

8

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



By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.