DDD 实战课
欧创新
人保资深架构师
55517 人已学习
新⼈⾸单¥59
登录后,你可以任选2讲全文学习
课程目录
已完结/共 26 讲
开篇词 (1讲)
DDD 实战课
15
15
1.0x
00:00/00:00
登录|注册

05 | 聚合和聚合根:怎样设计聚合?

不可变
无ID
状态可变
有ID标识
聚合内部的管理者
具有全局唯一标识
最小微服务单位
高内聚、低耦合
通过聚合根ID关联引用
聚合之间的接口人
聚合的管理者
实体本身
避免数据不一致性问题
实现核心业务逻辑
包含多个聚合
业务逻辑高内聚
包含实体和值对象
高内聚、低耦合
每个聚合对应一个仓储
数据修改和持久化的基本单元
由实体和值对象组合而成
描述实体状态和特征
业务行为
业务属性
值对象的特点
实体的特点
聚合根的特点
聚合的特点
应用层实现跨聚合的服务调用
边界外使用最终一致性
通过唯一标识引用其它聚合
设计小聚合
一致性边界内建模不变条件
引用方式
聚合根的角色
目的
领域层
聚合内部
聚合
值对象
实体
总结
设计原则
聚合根
聚合
聚合和聚合根设计

该思维导图由 AI 生成,仅供参考

你好,我是欧创新。今天我们来学习聚合(Aggregate)和聚合根(AggregateRoot)。
我们先回顾下上一讲,在事件风暴中,我们会根据一些业务操作和行为找出实体(Entity)或值对象(ValueObject),进而将业务关联紧密的实体和值对象进行组合,构成聚合,再根据业务语义将多个聚合划定到同一个限界上下文(Bounded Context)中,并在限界上下文内完成领域建模。
那你知道为什么要在限界上下文和实体之间增加聚合和聚合根这两个概念吗?它们的作用是什么?怎么设计聚合?这就是我们这一讲重点要关注的问题。

聚合

在 DDD 中,实体和值对象是很基础的领域对象。实体一般对应业务对象,它具有业务属性和业务行为;而值对象主要是属性集合,对实体的状态和特征进行描述。但实体和值对象都只是个体化的对象,它们的行为表现出来的是个体的能力。
那聚合在其中起什么作用呢?
举个例子。社会是由一个个的个体组成的,象征着我们每一个人。随着社会的发展,慢慢出现了社团、机构、部门等组织,我们开始从个人变成了组织的一员,大家可以协同一致的工作,朝着一个最大的目标前进,发挥出更大的力量。
领域模型内的实体和值对象就好比个体,而能让实体和值对象协同工作的组织就是聚合,它用来确保这些领域对象在实现共同的业务逻辑时,能保证数据的一致性。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

领域驱动设计(DDD)中的聚合和聚合根是领域模型中的重要概念,本文深入浅出地介绍了它们的概念及设计原则。聚合是业务和逻辑紧密关联的实体和值对象组合而成的基本单元,而聚合根则是聚合的管理者,负责协调实体和值对象完成共同的业务逻辑。文章还总结了一些聚合的设计原则,为读者提供了在实际项目中设计聚合时的指导。此外,文章还探讨了聚合、聚合根、实体和值对象之间的联系和区别,以及它们在微服务架构中的应用。通过深入浅出的讲解,读者能够快速了解聚合和聚合根的概念及设计原则,为实际项目中的领域建模提供了有益的指导。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《DDD 实战课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(159)

  • 最新
  • 精选
  • 南山
    置顶
    老师,麻烦有空帮忙看一下 场景:电销 根据任务类型的属性创建具体的定期执行任务,调度器把到点的任务放到执行器里去执行。执行完了等待下一次执行,对任务生成的明细可以填写沟通记录(第三方服务)但是本服务要提供此字段查询 过程中记录统计日志。每个任务类型在查询他的任务时查询和列表展示字段集合都不一样 任务执行过程:从数据源获取数据 -> 根据任务配置的过滤规则过滤 -> 生成任务明细(有过期时间) -> 1.自动分配给销售/2.销售主动去领单,过程中生成任务执行日志,统计日志 领域对象:任务类型、任务、任务明细、日志、过滤规则、字段、销售、 聚合A:聚合根-任务,任务明细-实体,值对象:日志、任务类型、销售 聚合B:聚合根-过滤规则, 聚合C:聚合根-字段,值对象,展示字段属性定义集合、查询字段属性定义集合 问题: 1.一个聚合中,允不允许只有一个实体和一些属性值? 2.是否合理?不合理该怎么设计呢? 3.任务是根据任务类型创建出来的,聚合A是不是不合理?或者实体的初始化可不可以依赖它的值对象呢? 4.任务类型决定它可以进行哪些过滤,实际创建任务时才会真正选择使用哪些过滤规则,过滤规则算什么呢?能作为一个独立的聚合吗? 5.字段也是同样的,并且字段是没有一个生命周期的,这种情况下是不是作为值对象更合理? 但是聚合A和B都用到了字段,值对象能在多个聚合公用吗?

    作者回复: 我先理解一下你说的业务场景哈。不知道理解的对不对?不对的地方请你指出。由于不好展示事件风暴的过程,我就口述吧。 你描述的业务场景主要包括这两个部分吧。 流程一:创建任务 1、根据任务规则获取任务基础数据,生成任务。(产生任务已创建事件,领域对象包括:任务生成的基础数据、数据过滤规则、任务、任务类型) 2、自动将任务分配给销售。(销售的数据应该来源于其他系统,这个过程实际上是一个给任务赋值的过程,领域对象包括:销售) 3、销售领取任务。(给任务分配销售) 这个过程领域对象包括:基础数据、数据过滤规则、任务、任务类型、销售。 命令有:创建任务,给任务分配销售。 领域事件有:任务已创建。 流程二、任务执行 1、销售查询并获取任务,执行任务。(不清楚你说的字段在这个过程是什么含义,是查询时勾选类型吗) 2、任务执行完成后,记录执行结果,产生任务执行日志。(产生任务执行日志已创建事件,领域对象有:任务、任务日志) 3、统计日志。这一块不清楚你的业务逻辑和流程。 因为有统计日志,是不是就会去查询任务的执行日志,如果是这样的话,任务日志就需要设计为实体,跟任务关联,这里任务是聚合根。 这个阶段的领域对象有:任务和任务执行日志。 命令有:查询任务,生成任务执行日志。 领域事件有:执行任务日志已创建。 结合这两个流程,我们整体来分析一下。 领域对象包括:基础数据、数据过滤规则、任务、销售、任务类型、任务执行日志。 由于销售人员数据来源于第三方,以值的形式存储在任务中,因此我们可以将它设计为任务的值对象。 任务执行日志依附于任务,但是由于它后续要做查询和统计分析,因此将它设计为被任务引用的实体。 任务类型是任务的值对象 其它的基础数据、数据过滤规则这两个领域对象很独立,你可以理解他们是独立的实体,或者说一个实体就是一个聚合。 但是这样设计在代码目录设计时会显得比较单薄,一个聚合会有一个仓储和聚合自己的代码目录结构。因此我们可以将这两个实体可以直接放在任务的聚合里,但是他们的生命周期不受任务这个聚合根管理。 这样的话,我们就可以只建立一个任务聚合。这个聚合的聚合根是任务。它引用的实体包括任务执行日志,任务的值对象有:销售、任务类型。还有两个独立实体:基础数据和数据过滤规则。 说明一下: 在不少的数据统计和计算场景中,有很多实体之间相互独立,只参与计算和统计分析,但是这类场景中业务内聚性又很高,你找不出管理这些实体的聚合根。我称这种业务模型是非典型领域模型。虽然有些方面(比如聚合根)不符合DDD的一些原则,但是我们也可以按照DDD方法来完成设计。

    2019-10-23
    6
    134
  • 胖虎
    老师,能用电商的例子说一下聚合根的使用场景嘛

    作者回复: 电商里面比较典型的几个聚合根,比如:库存、商品、订单等等。 以订单为例,订单在聚合里是聚合根,与订单关联的有订单明细和收货地址。订单明细包括商品ID,商品名称,价格以及数量等信息,由于订单明细是多个,它是一个集合,它被设计为实体,被订单引用。而订单只有一个收货地址,这个收货地址的值来源于你个人中心维护的收货地址,收货地址只能被整体替换,所以它被设计为值对象。

    2019-10-31
    8
    42
  • stg609
    首先,几乎每个留言都会评论!必须给个大大的赞! 其次,我有3个疑问, 1. 聚合中可能会有实体,那允不允许直接把实体类型作为属性的类型或返回值暴露给外界? 2. 如果允许,那外界就可以直接获取其中的实体,然后调用者可以直接使用实体中的一些相关方法?这样似乎就违反了聚合很的设计? 3. 如果不允许,那外界如果需要得到某个实体的数据,要怎么操作?封装成 DTO 吗? 比如: Aggregate a 中包含一个b属性,b是一个实体。b 中包含操作该实体的方法 M。那外界调用a.b 就会直接获取到 b 这个实体,然后可以直接调用 M 方法。

    作者回复: 非常感谢! 首先你说的外界,要明确一下,这个外界是微服务内,不同的层之间?还是指微服务之间,也就是微服务外部调用。 如果是在微服务内,不同层之间的话,由于运行在同一个微服务内,实体是可以被其它高层服务获取的,这时实体以DO对象的形式,存在于应用层和领域层,你可以使用DO对象的方法。但是不建议将实体的方法编排放在应用层,而是在领域层封装成领域服务后暴露给应用层。 如果这个外界是微服务之间的话,你需要将实体DO数据转换为DTO对象后,才能被外界使用,这时,外界是不能调用实体的方法的,因此可以隐藏实体核心业务逻辑的实现方式。

    2019-10-29
    25
  • 渊虹
    老师,有个问题不明白,麻烦解惑。投保聚合和客户聚合中,投保人和被保人跨聚合引用到客户的id,需求是查询以客户s为被保人的保单。就需要跨过聚合根,直接访问被保人这个值对象。这个是不是和只能通过聚合根访问聚合内其他对象的理论不一致

    作者回复: 被保人这个值对象以属性嵌入的方式嵌入保单聚合根中,查询客户保单时你不需要到客户聚合去查询客户信息了,直接根据客户信息在投保聚合查保单就可以了,当然这个客户信息不只是ID。 说明一下:这个跨聚合引用是在生成保单的时候,通过客户聚合根查询获取的客户信息,从客户聚合获取客户信息后,客户的信息就作为值对象的值嵌入到了保单实体中。

    2019-10-24
    14
    24
  • 密码123456
    聚合。用界限上下文把细粒度的实体圈起来当做一个组织,选出组织的董事长。一个组织和另外一个组织交流的时候,只需要通过一个董事长,能够了解该组织的全部非隐私信息

    作者回复: 类比的不错。

    2019-10-23
    15
  • 李二木
    什么是充血模型

    作者回复: 说到充血模型,就离不开贫血模型。 先说一下贫血模型吧,贫血模型是指使用的领域对象中只有setter和getter方法,所有的业务逻辑都不包含在领域对象中而是放在业务逻辑层。 而充血模型将大多数业务逻辑放在领域实体中实现,实体本身包含了属性和它的业务行为,它在领域模型中就是一个具有业务行为和逻辑的基本业务单元。

    2019-10-23
    5
    13
  • 美美
    老师,想请教一下 我们的场景是:商家后台上,商家可以进行门店管理,收银pos机相关的设置,营业相关的设置 背景:商家后台已经存在,且商家基础系统、门店基础信息、pos相关信息,数据模型已经存在,各调用方是直接访问数据库的 目标:将商家、门店的基础信息,经营设置信息,抽象出来形成商家域 统一对外提供能力 想通过DDD的思想来进行建模,感觉无从下手,麻烦老师提供点思路

    作者回复: 后面会有建模方法的介绍。 你可以试着从用户旅程入手,根据流程看看会发生哪些领域事件,再找找是哪些命令触发这些领域事件的。梳理的差不多的时候,你就找找这些命令都是哪些实体的行为,这样就可以找出好多实体。根据实体可以找出聚合根,再根据聚合根找出关联的实体和值对象。这样聚合就找到了,然后对聚合划分限界上下文。这样就可以设计微服务了。

    2019-11-06
    2
    12
  • hunter
    跨多个实体的业务逻辑通过领域服务来实现。 其次它作为聚合的管理者,在聚合内部负责协调实体和值对象按照固定的业务规则协同完成共同的业务逻辑。 怎么感觉 聚合根做的事情和领域服务一样?

    作者回复: 两个出发点不一样,聚合根主要是从实体关联的角度,关注数据一致性。领域服务主要组合的是实体业务行为。

    2019-10-29
    12
  • Geek_7c4953
    老师,对于关联两个聚合根的一种关系,应该划分到哪个聚合? 比如: 用户聚合根和课程聚合根。 用户收藏了一门课程,收藏这个操作关联了用户和课程,同时生成了一条收藏记录。 那么收藏记录是值对象还是实体,应该归于哪个聚合根? 如果作为实体,它似乎不需要ID去标识,因为一个用户ID,一个课程ID就可以标识这样一条记录。 如果作为值对象,又感觉不合适,因为我的感觉(仅仅是感觉)多值的值对象不应该能无限增长,但收藏这个操作是不可能限制数量的。

    作者回复: 我感觉收藏应该也是一个独立聚合,收藏会有聚合根,它会引用用户ID和课程ID这两个值对象。这样可以对用户和课程独立管理和维护,保证用户聚合和课程聚合的领域逻辑稳定。用户登录时,获取用户ID,然后将用户ID值对象作为查询条件从收藏聚合获取收藏记录,再通过收藏记录关联的课程ID从课程聚合获取课程的详细数据。

    2020-08-14
    3
    8
  • 蜗牛慢慢爬
    听了这么多节课,总结一下还是太抽象了

    作者回复: 基础篇主要讲解DDD的基础概念和设计理念,所以相对抽象一些。后面会有中台业务建模和微服务设计案例,比较好理解。敬请期待。

    2019-10-23
    2
    8
收起评论
显示
设置
留言
99+
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部