DDD核心知识体系

  • 概述
  • 领域
  • 领域
  • 子域
  • 总结
  • 限界上下文
  • 通用语言
  • 限界上下文
  • 总结
  • 领域对象
  • 实体
  • 实体的业务形态
  • 实体的代码形态
  • 实体的运行形态
  • 实体的数据库形态
  • 值对象
  • 值对象的业务形态
  • 值对象的代码形态
  • 值对象的运行形态
  • 值对象的数据库形态
  • 值对象的优势和局限
  • 实体与值对象的关系
  • 聚合和聚合根
  • 聚合
  • 聚合根
  • 如何设计聚合
  • 聚合的设计原则


概述

DDD的核心知识体系主要包括领域、子域、核心域、支撑域、通用域、限界上下文、实体、值对象、聚合、聚合根等概念。

ddd领域模型设计java示例 ddd领域模型实战_值对象

领域

领域

  • 领域就是用来确定范围的。范围就是边界,这就是DDD不断强调边界的原因。
  • DDD的领域就是这个边界内要解决的业务问题域。在研究和解决业务问题时,DDD会按照一定的规则将业务领域进行细分,当领域细分到一定程度后,DDD会将问题范围限定在特定的边界内,在这个边界内建立领域模型,进而用代码实现领域模型,解决相应业务问题。
  • 领域越大,业务范围越大。领域是用来限定边界和范围的,那么就有大小之分,领域越大业务范围就越广。

子域

  • 领域进一步划分为子领域,我们把划分出来的子领域称为子域,每个子域对应一个更小的问题或更小的业务范围。
  • 每一个细分的领域都会有一个领域模型,所有的子域研究完成了也就建立了全域的领域模型。
  • 领域模型的核心思想就是将问题域逐步分解,降低业务理解和系统实现的复杂度。

领域划分:

  • 由于公司在IT系统建设过程中,预算和资源有限,对不同类型的子域应该有不同的关注度和资源投入策略。
  • 商业模式和战略方向不同也会导致划分出现差异,在公司领域细分、建立领域模型和系统建设时,需要根据公司的战略重点和商业模式找到核心域,并重点关注核心域。

总结

  • 领域的核心思想就是讲问题域逐级细分,降低业务理解和系统实现的复杂度。通过领域细分逐步缩小问题域,构建合适的领域模型
  • 核心域、支撑域和通用域的目标:通过领域划分,区分不同子域在公司内的不同功能属性和重要性,从而公司可对不同子域采取不同的资源投入和建设策略,关注度也会不一样。

限界上下文

  • 在DDD建模和系统建设中有很多参与者,不同的参与者对同样的领域知识会有不同的理解,这样就造成交流障碍,基于这个原因,DDD就出现了***通用语言***和***限界上下文***两个概念
  • 通用语言定义上下文含义,限界上下文定义领域边界,保证每个上下文的含义在特定的边界内有唯一含义,领域模型则存在这个边界之内。

通用语言

  • 通用语言是团队统一的语言,统一领域的软件生命周期中使用统一的语言进行交流。
  • 通用语言包括术语和用例场景,并且能够直接反映在代码中。如商品、订单等对应实体对象,动词则表示一个动作或时间对应领域事件或命令。
  • 通用语言贯穿DDD整个设计过程,作为项目团队沟通和协商的统一语言,基于它才能开发出可读性更好的代码,将业务需求转化为代码设计。

限界上下文

  • 限界上下文可以拆解成限界和上下文两部分。限界就是领域的边界,上下文则是语义环境,通过领域的限界上下文可以统一领域边界内的语言,并用统一语言进行交流。

总结

  • 通用语言是项目团队内部交流的统一语言,这个语言的语义环境就是限界上下文限定的,以确保语义唯一性。
  • 限界上下文确定了程序设计和拆分的方向。如果不考虑技术异构、团队沟通等外部因素,一个限界上下文理论上可以设计为一个模块或一个微服务。

领域对象

  • 实体和值对象是领域模型中的值对象,涉及到代码与数据模型的映射。
  • 实体和值对象在不同的阶段形态不同,这个与设计和代码实现密切相关。

实体

  • 在DDD中,拥有唯一标识符,且标识符在历经各种状态变更后仍能保持一致,对这些对象而言重要的不是其属性,而是延续性和标识,对象的延续性和标识会跨越甚至超出软件的生命周期,这样的对象我们称为实体。

实体的业务形态

  • 在DDD不同的设计过程中,实体形态是不同的。领域模型中的实体是多个属性、操作或行为的载体。

实体的代码形态

  • 在代码模型中,实体的表现形式就是实体类,这个类包含了实体的属性和方法,通过这些方法实现实体自身的业务逻辑。
  • 在DDD中,这些实体类通常采用充血模型,与这个实体相关的所有业务逻辑都在这个实体类的方法中实现,跨多个实体的领域逻辑则在领域服务中实现。

实体的运行形态

  • 实体以DO(领域对象)的形式存在,每个实体对象都有唯一的ID。我们可以对一个实体对象进行多次修改,修改后的数据和原来的数据可能会大不相同。但是,它们拥有相同的ID,它们依然是同一个实体。
  • 商品是商品上下文的一个实体,通过唯一的商品ID来标识,不管商品数据如何变化,ID一直不变,他始终是同一个商品。

实体的数据库形态

  • DDD是先构建领域模型,然后针对实际的业务场景构建实体对象和行为,再将这些实体对象映射到数据持久化对象。
  • 领域模型映射到数据模型时,一个实体可能对应0个、1个或多个数据库持久化对象。大部分情况下一对一的关系。某些场景中,有些实体知识暂驻内存中的一个运行态实体不需持久化。
  • 在一些复杂场景下,实体和持久化对象可能是一对多或多对一的关系。

值对象

  • 值对象是通过对象属性来识别的对象,它将多个相关属性组合为一个概念整体。在DDD中用来描述领域的特定方面,并且是一个没有标识符的对象叫值对象。
  • 值对象就是一个集合,多个用于描述目的,具有整体概念和不可修改的属性。
  • 在领域建模过程中,值对象可以保证属性归类的清晰和概念的完整性,避免属性零碎。

值对象的业务形态

  • 值对象是DDD领域模型中的一个基础对象,包含了若干个属性,与实体构成聚合。
  • 值对象是若干属性的集合,只有数据初始化操作和有限的不涉及修改数据的行为,基本不包含业务逻辑。
  • 值对象的属性集虽然在物理上独立出来,但逻辑上是仍属于实体属性的一部分,用于描述实体特征。

值对象的代码形态

  • 值对象在代码中有两种形态。如果值对象是单一属性,则直接定义为实体类的属性;如果值对象是属性集合则设计为类,类将具有整体概念的多个属性归集到属性集合,这样的值对象没有ID会被实体整体引用。

值对象的运行形态

  • 值对象嵌入到实体,有两种不同的数据格式,分别是属性嵌入的方式和序列化大对象的方式。
  • 嵌入式方式:引用单一属性的值对象或只有一条记录的多属性值对象实体采用。
  • 序列化大对象:引用一条或多条属性值对象的实体。

值对象的数据库形态

  • 值对象在数据库持久化方面简化了设计,它的数据库设计大多采用非数据库范式,值对象的属性值和实体对象的属性值保存在同一个数据库实体表中。
  • 在领域建模时,我们可以将部分对象设计为值对象,保留对象的业务含义,同时减少实体数量;
  • 在数据建模时,我们将值对象嵌入实体,减少实体表数量,简化数据库设计。

值对象的优势和局限

  • 值对象采用序列化大对象的方法简化了数据库设计,减少了实体表数量,可以简单、清晰表达业务概念。这种设计降低了数据库设计复杂度,却无法满足值对象的快速查询,导致搜索变得困难。
  • 值对象采用属性嵌入的方法提升了数据库性能,但如果实体引用的值对象过多,则会导致实体堆积一堆缺乏概念完整性的属性,这样值对象就失去业务含义,操作起来不变。

实体与值对象的关系

  • 实体和值在某些场景下可以互换,有的时候很难判断将领域对象设计为实体还是值对象。
  • 实体一般对应业务对象,具有业务属性和业务行为
  • 值对象主要是属性集合,对实体状态和特征进行描述

聚合和聚合根

聚合

  • 领域模型中的实体和值对象都是个体,而让实体和值对象协同工作的组织就是聚合。
  • 聚合用来确保领域对象在实现共同的业务逻辑时,能保证数据一致性。
  • 聚合是由业务和逻辑紧密关联的实体和值对象组合而成,聚合是数据修改和持久化的基本单元,每一个聚合对应一个仓储,实现数据持久化。
  • 聚合有一个聚合根和上下文边界,边界根据业务单一职责和高内聚原则定义聚合内有哪些实体和对象,聚合之间是松耦合。
  • 聚合在DDD分层结构中数据领域层,领域层包含多个聚合,共同实现业务逻辑。跨多个实体的业务逻辑通过领域服务来实现,跨多个聚合的业务逻辑通过应用服务来实现。

聚合根

  • 聚合根的目的是避免由于复杂数据模型缺少统一的业务规则控制,而导致聚合、实体之间数据不一致性的问题。
  • 如果把聚合比作组织,聚合根就是组织负责人。聚合根成为跟实体,它不仅是实体还是聚合的管理者。
  • 聚合根作为聚合的管理者,再聚合内部负责协调实体和值对象按照固定的业务规则协同完成共同的业务逻辑。
  • 在聚合之间,聚合根还是对外接口人,聚合根通过ID关联的方式接受外部任务和请求,在上下文内实现聚合之间的业务协同。
  • 聚合之间通过聚合根ID关联引用,如果需要访问其他聚合的实体,就要先访问聚合根,再导航到聚合内部实体,外部对象不能直接访问聚合内实体。

如何设计聚合

  • 梳理所有的实体和值对象
  • 从众多实体中选出适合作为对象管理者的跟实体,也就是聚合根。

判断一个实体是否是聚合根的条件:

  • 是否有独立的生命周期,
  • 是否有全局唯一的ID,
  • 是否可以创建或修改其他对象,
  • 是否有专门的模块来管理这个实体。
  • 根据单一职责和高内聚原则,找出与聚合根关联的所有紧密依赖的实体和值对象。构建出1个包含聚合根(唯一),多个实体和值对象的对象集合,这个集合就是聚合。
  • 在聚合内根据聚合根、实体和对象的依赖,画出对象的引用和依赖模型。
  • 多个聚合根据业务语义和上下文一起划分到同一个限界上下文中。

聚合的设计原则

  • 在一致性边界内建模真正不变的条件。聚合用来封装真正的不变性,而不是简单的将对象组合。聚合内有一套不变的业务规则,实体和值对象按照统一的业务规则运行,实现对象数据的一致性,边界外的任何东西都与该聚合无关。
  • *** 设计小聚合**。设计过大会因为实体过多导致实体间管理过于复杂,高频操作会出现并发冲突或数据库锁,导致可用性变差。小聚合则可以降低业务过大导致重构可能,让领域模型更能适应业务变化。
  • 通过唯一表示引用其他聚合。聚合之间通过关联聚合根ID方式引用,不是直接对象引用。
  • 在边界之外最终一致性。聚合内数据强一致,聚合间数据最终一致。如果一次业务操作涉及多个聚合状态更改,应采用领域事件异步修改相关聚合,实现聚合之间解耦。
  • 通过应用层实现跨聚合的服务调用。应该避免聚合的领域服务调用和跨聚合的数据表关联。