05 | 聚合和聚合根:怎样设计聚合?
该思维导图由 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-236134 - 胖虎老师,能用电商的例子说一下聚合根的使用场景嘛
作者回复: 电商里面比较典型的几个聚合根,比如:库存、商品、订单等等。 以订单为例,订单在聚合里是聚合根,与订单关联的有订单明细和收货地址。订单明细包括商品ID,商品名称,价格以及数量等信息,由于订单明细是多个,它是一个集合,它被设计为实体,被订单引用。而订单只有一个收货地址,这个收货地址的值来源于你个人中心维护的收货地址,收货地址只能被整体替换,所以它被设计为值对象。
2019-10-31842 - stg609首先,几乎每个留言都会评论!必须给个大大的赞! 其次,我有3个疑问, 1. 聚合中可能会有实体,那允不允许直接把实体类型作为属性的类型或返回值暴露给外界? 2. 如果允许,那外界就可以直接获取其中的实体,然后调用者可以直接使用实体中的一些相关方法?这样似乎就违反了聚合很的设计? 3. 如果不允许,那外界如果需要得到某个实体的数据,要怎么操作?封装成 DTO 吗? 比如: Aggregate a 中包含一个b属性,b是一个实体。b 中包含操作该实体的方法 M。那外界调用a.b 就会直接获取到 b 这个实体,然后可以直接调用 M 方法。
作者回复: 非常感谢! 首先你说的外界,要明确一下,这个外界是微服务内,不同的层之间?还是指微服务之间,也就是微服务外部调用。 如果是在微服务内,不同层之间的话,由于运行在同一个微服务内,实体是可以被其它高层服务获取的,这时实体以DO对象的形式,存在于应用层和领域层,你可以使用DO对象的方法。但是不建议将实体的方法编排放在应用层,而是在领域层封装成领域服务后暴露给应用层。 如果这个外界是微服务之间的话,你需要将实体DO数据转换为DTO对象后,才能被外界使用,这时,外界是不能调用实体的方法的,因此可以隐藏实体核心业务逻辑的实现方式。
2019-10-2925 - 渊虹老师,有个问题不明白,麻烦解惑。投保聚合和客户聚合中,投保人和被保人跨聚合引用到客户的id,需求是查询以客户s为被保人的保单。就需要跨过聚合根,直接访问被保人这个值对象。这个是不是和只能通过聚合根访问聚合内其他对象的理论不一致
作者回复: 被保人这个值对象以属性嵌入的方式嵌入保单聚合根中,查询客户保单时你不需要到客户聚合去查询客户信息了,直接根据客户信息在投保聚合查保单就可以了,当然这个客户信息不只是ID。 说明一下:这个跨聚合引用是在生成保单的时候,通过客户聚合根查询获取的客户信息,从客户聚合获取客户信息后,客户的信息就作为值对象的值嵌入到了保单实体中。
2019-10-241424 - 密码123456聚合。用界限上下文把细粒度的实体圈起来当做一个组织,选出组织的董事长。一个组织和另外一个组织交流的时候,只需要通过一个董事长,能够了解该组织的全部非隐私信息
作者回复: 类比的不错。
2019-10-2315 - 李二木什么是充血模型
作者回复: 说到充血模型,就离不开贫血模型。 先说一下贫血模型吧,贫血模型是指使用的领域对象中只有setter和getter方法,所有的业务逻辑都不包含在领域对象中而是放在业务逻辑层。 而充血模型将大多数业务逻辑放在领域实体中实现,实体本身包含了属性和它的业务行为,它在领域模型中就是一个具有业务行为和逻辑的基本业务单元。
2019-10-23513 - 美美老师,想请教一下 我们的场景是:商家后台上,商家可以进行门店管理,收银pos机相关的设置,营业相关的设置 背景:商家后台已经存在,且商家基础系统、门店基础信息、pos相关信息,数据模型已经存在,各调用方是直接访问数据库的 目标:将商家、门店的基础信息,经营设置信息,抽象出来形成商家域 统一对外提供能力 想通过DDD的思想来进行建模,感觉无从下手,麻烦老师提供点思路
作者回复: 后面会有建模方法的介绍。 你可以试着从用户旅程入手,根据流程看看会发生哪些领域事件,再找找是哪些命令触发这些领域事件的。梳理的差不多的时候,你就找找这些命令都是哪些实体的行为,这样就可以找出好多实体。根据实体可以找出聚合根,再根据聚合根找出关联的实体和值对象。这样聚合就找到了,然后对聚合划分限界上下文。这样就可以设计微服务了。
2019-11-06212 - hunter跨多个实体的业务逻辑通过领域服务来实现。 其次它作为聚合的管理者,在聚合内部负责协调实体和值对象按照固定的业务规则协同完成共同的业务逻辑。 怎么感觉 聚合根做的事情和领域服务一样?
作者回复: 两个出发点不一样,聚合根主要是从实体关联的角度,关注数据一致性。领域服务主要组合的是实体业务行为。
2019-10-2912 - Geek_7c4953老师,对于关联两个聚合根的一种关系,应该划分到哪个聚合? 比如: 用户聚合根和课程聚合根。 用户收藏了一门课程,收藏这个操作关联了用户和课程,同时生成了一条收藏记录。 那么收藏记录是值对象还是实体,应该归于哪个聚合根? 如果作为实体,它似乎不需要ID去标识,因为一个用户ID,一个课程ID就可以标识这样一条记录。 如果作为值对象,又感觉不合适,因为我的感觉(仅仅是感觉)多值的值对象不应该能无限增长,但收藏这个操作是不可能限制数量的。
作者回复: 我感觉收藏应该也是一个独立聚合,收藏会有聚合根,它会引用用户ID和课程ID这两个值对象。这样可以对用户和课程独立管理和维护,保证用户聚合和课程聚合的领域逻辑稳定。用户登录时,获取用户ID,然后将用户ID值对象作为查询条件从收藏聚合获取收藏记录,再通过收藏记录关联的课程ID从课程聚合获取课程的详细数据。
2020-08-1438 - 蜗牛慢慢爬听了这么多节课,总结一下还是太抽象了
作者回复: 基础篇主要讲解DDD的基础概念和设计理念,所以相对抽象一些。后面会有中台业务建模和微服务设计案例,比较好理解。敬请期待。
2019-10-2328