我正试图了解如何正确使用存储库模式。 Aggregate Root的核心概念不断涌现。在搜索Web和Stack Overflow以获取有关聚合根的帮助时,我会不断发现有关它们的讨论以及指向应该包含基本定义的页面的死链接。
在存储库模式的上下文中,什么是聚合根?
答案 0 :(得分:271)
在存储库模式的上下文中,聚合根是客户端代码从存储库加载的唯一对象。
存储库封装了对子对象的访问 - 从调用者的角度来看,它会自动加载它们,无论是在加载根目录还是实际需要它们时(如延迟加载)。
例如,您可能有一个Order
对象,它封装了多个LineItem
对象上的操作。您的客户端代码永远不会直接加载LineItem
个对象,只会加载包含它们的Order
,这将是您域中该部分的聚合根。
答案 1 :(得分:176)
来自Evans DDD:
AGGREGATE是一组关联对象,我们将其视为一个单元,用于数据更改。每个AGGREGATE都有一个根和一个边界。边界定义了AGGREGATE中的内容。根是AGGREGATE中包含的单个特定ENTITY。
和
根是AGGREGATE的唯一成员,允许外部对象保存对[。]的引用
这意味着聚合根是唯一可以从存储库加载的对象。
示例是包含Customer
实体和Address
实体的模型。我们永远不会直接从模型访问Address
实体,因为没有相关Customer
的上下文就没有意义。因此,我们可以说Customer
和Address
一起形成聚合,Customer
是聚合根。
答案 2 :(得分:80)
聚合根是简单想法的复杂名称。
设计良好的类图封装了其内部结构。您访问此结构的点称为aggregate root
。
您的解决方案的内部可能非常复杂,但此层次结构的用户只会使用root.doSomethingWhichHasBusinessMeaning()
。
你想怎么骑车?选择更好的api
选项A(它只是某种程度上有效):
car.ride();
选项B(用户可以访问类inernals):
if(car.getTires().getUsageLevel()< Car.ACCEPTABLE_TIRE_USAGE)
for (Wheel w: car:getWheels()){
w.spin();
}
}
如果您认为选项A更好,那么祝贺您。你得到aggregate root
背后的主要原因。
聚合根封装了多个类。你只能通过主对象来操纵整个层次结构。
答案 3 :(得分:31)
想象一下,你有一个计算机实体,这个实体也离不开它的软件实体和硬件实体。这些构成了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 {}
请记住,硬件也可能是一个ValueObject(没有自己的身份),仅作为示例。
答案 4 :(得分:11)
如果您遵循数据库优先方法,则聚合根通常是1-many关系的1侧的表。
最常见的例子是人。每个人都有许多地址,一张或多张工资单,发票,CRM条目等。情况并非总是如此,但却是9/10倍。
我们目前正在开发一个电子商务平台,我们基本上有两个聚合根源:
客户提供联系信息,我们为他们分配交易,交易获取订单项等
卖家销售产品,联系人,关于我们页面,特别优惠等。
这些由客户和卖方存储库分别负责。
答案 5 :(得分:8)
答案 6 :(得分:7)
答案 7 :(得分:3)
汇总表示收集某些内容
root 就像树的顶级节点,我们可以从中访问网页文档中<html>
节点之类的所有内容。
Blog类比,用户可以有很多帖子,每个帖子都可以有很多评论。因此,如果我们获取任何用户,那么它可以充当 root 来访问所有相关帖子以及这些帖子的进一步评论。这些都被称为集合或聚合
答案 8 :(得分:1)
聚合是保护不变量的地方,通过限制其访问思想聚合根来强制一致性。不要忘记,聚合应根据您的项目业务规则和不变量进行设计,而不是数据库关系。你不应该注入任何存储库,不允许任何查询。
答案 9 :(得分:0)
在Erlang中,一旦聚合由状态内的数据结构组成,而不是OO组合,则无需区分聚合。查看示例:https://github.com/bryanhunter/cqrs-with-erlang/tree/ndc-london
答案 10 :(得分:0)
在另一个世界中,在事件源中,“聚合(根)”是一个不同的概念。 事件源可能与CQRS,DDD等一起遇到。
在事件采购中,聚合是一个对象,其状态(字段)未映射到数据库中的记录,就像我们在SQL / JPA世界中通常认为的那样。
不是一组相关实体。
这是一组相关记录,例如历史记录表中的
。GiftCard.amount是GiftCard汇总中的一个字段,但是此字段映射到所有事件,例如曾经创建过的兑现卡(从卡中取出钱)。
因此,聚合的数据源不是数据库中的记录,而是为该特定聚合创建的事件的完整列表。我们说我们是事件来源的汇总。
现在,我们可以问自己这是怎么做的?谁在汇总这些事件,因此我们仍在使用一个字段(例如GiftCard.amount)进行操作?我们可能希望该金额是一个Collection而不是大十进制类型。
由事件源引擎来完成工作,他们可以简单地按创建顺序重播所有事件。但这超出了此线程的范围。