DDD-基础概念

DDD基础概念比较多,都是新的名词,刚开始接触会比较混乱。而且有些名词也比较晦涩难懂,这里统一学习整理DDD相关的基础概念。

这些基础概念,在架构设计、业务建模和实际开发过程中不一定用到,或不全用到。但是这些概念可以帮助理解DDD的核心思想和理念,在实际操作中可以借鉴。

基本概念

主要包括领域、子域、核心域、通用域、支撑域、限界上下文、聚合、聚合根、实体、值对象等等

领域和子域

领域是用来限定业务边界和范围的,领域越大,业务范围就越大,反之则相反。

领域可以进一步划分为子领域。划分出来的多个子领域称为子域,每个子域对应一个更小的问题域或更小的业务范围。

领域划分通常的做法就是将问题一步一步地细分,再针对细分出来的问题域,逐个深入研究,探索和建立所有子域。

核心域、通用域和支撑域

在领域不断划分的过程中,领域会细分为不同的子域,子域可以根据自身重要性和功能属性划分为三类子域:核心域、通用域和支撑域

核心域:业务的核心子域。包含核心的业务特性

通用域:具有通用功能的子域。可以同时被多个子域使用

支撑域:既不包含核心的业务特性,也不包含通用功能的子域。包含非核心的业务特性

限界上下文

通用语言

在事件风暴过程中,通过团队交流达成共识的,能够简单、清晰、准确描述业务涵义和规则的语言就是通用语言

通用语言是团队统一的语言,不管你在团队中承担什么角色,在同一个领域的软件生命周期里都使用统一的语言进行交流

通用语言作用是解决交流障碍问题,确保业务需求的正确表达。

限界上下文

通用语言是有语义环境的,限界上下文是用来确定语义所在的领域边界

限界上下文 = 限界 + 上下文,限界:领域边界,上下文:语义环境

通过领域的限界上下文,我们就可以在统一的领域边界内用统一的语言进行交流

通用语言确定了项目团队内部交流的统一语言,而这个语言所在的语义环境则是由限界上下文来限定的,以确保语义的唯一性

实体和值对象

实体和值对象是组成领域模型的基础单元

实体

实体一般对应业务对象,它具有业务属性和业务行为

  • 业务形态:按照一定的业务规则将依存度高和业务关联紧密的多个实体对象和值对象进行聚类,形成聚合

  • 代码形态:在DDD里,这些实体类通常采用充血模型,与这个实体相关的所有业务逻辑都在实体类的方法中实现,跨多个实体的领域逻辑则在领域服务中实现

  • 运行形态:每个实体对象都有唯一的ID,并且ID始终保持不变

  • 数据库形态:在领域模型映射到数据模型时,一个实体可能对应0个、1个或者多个数据库持久化对象。大多数情况下实体与持久化对象是一对一。在某些场景中,有些实体只是暂驻静态内存的一个运行态实体,它不需要持久化

值对象

值对象主要是属性集合,对实体的状态和特征进行描述

通过对象属性值来识别的对象,它将多个相关属性组合为一个概念整体。在DDD中用来描述领域的特定方面,并且是一个没有标识符的对象,叫作值对象

值对象本质上就是一个集合。集合里包括若干个用于描述目的、具有整体概念和不可修改的属性。那这个集合存在的意义又是什么?在领域建模的过程中,值对象可以保证属性归类的清晰和概念的完整性,避免属性零碎

  • 业务形态:是领域模型中的一个基础对象,包含若干属性,与实体一起构成聚合
  • 代码形态:值对象是单一属性,则直接定义为实体类的属性;如果值对象是属性集合,则把它设计为Class类
  • 运行形态:两种方式:以属性嵌入的方式嵌入到实体对象中;以序列化后的JSON嵌入到实体对象
  • 数据库形态:在领域建模时,我们可以将部分对象设计为值对象,保留对象的业务涵义,同时又减少了实体的数量;在数据建模时,我们可以将值对象嵌入实体,减少实体表的数量,简化数据库设计

实体与值对象关系

实体和值对象是最基础的对象,一起实现实体最基本的核心领域逻辑

DDD提倡从领域模型设计出发,而不是先设计数据模型。传统的数据模型设计通常是一个表对应一个实体,一个主表关联多个从表,当实体表太多的时候就很容易陷入无穷无尽的复杂的数据库设计,领域模型就很容易被数据模型绑架

值对象的优势是简化数据库设计,更注重领域模型

聚合和聚合根

聚合

领域模型内的实体和值对象就好比个体,而能让实体和值对象协同工作的组织就是聚合,它用来确保这些领域对象在实现共同的业务逻辑时,能保证数据的一致性。

聚合在DDD分层架构里属于领域层,领域层包含了多个聚合,共同实现核心业务逻辑。聚合内实体以充血模型实现个体业务能力,以及业务逻辑的高内聚。跨多个实体的业务逻辑通过领域服务来实现,跨多个聚合的业务逻辑通过应用服务来实现。

聚合根

聚合根的主要目的是为了避免由于复杂数据模型缺少统一的业务规则控制,而导致聚合、实体之间数据不一致性的问题。

聚合根也称为根实体,它不仅是实体,还是聚合的管理者:

  • 首先它作为实体本身,拥有实体的属性和业务行为,实现自身的业务逻辑。

  • 其次它作为聚合的管理者,在聚合内部负责协调实体和值对象按照固定的业务规则协同完成共同的业务逻辑。

在聚合之间,它还是聚合对外的接口人,以聚合根ID关联的方式接受外部任务和请求,在上下文内实现聚合之间的业务协同。也就是说,聚合之间通过聚合根ID关联引用,如果需要访问其它聚合的实体,就要先访问聚合根,再导航到聚合内部实体,外部对象不能直接访问聚合内实体。

聚合设计原则

1. 在一致性边界内建模真正的不变条件。聚合用来封装真正的不变性,而不是简单地将对象组合在一起。聚合内有一套不变的业务规则,各实体和值对象按照统一的业务规则运行,实现对象数据的一致性,边界之外的任何东西都与该聚合无关,这就是聚合能实现业务高内聚的原因。

2. 设计小聚合。如果聚合设计得过大,聚合会因为包含过多的实体,导致实体之间的管理过于复杂,高频操作时会出现并发冲突或者数据库锁,最终导致系统可用性变差。而小聚合设计则可以降低由于业务过大导致聚合重构的可能性,让领域模型更能适应业务的变化。

3. 通过唯一标识引用其它聚合。聚合之间是通过关联外部聚合根ID的方式引用,而不是直接对象引用的方式。外部聚合的对象放在聚合边界内管理,容易导致聚合的边界不清晰,也会增加聚合之间的耦合度。

4. 在边界之外使用最终一致性。聚合内数据强一致性,而聚合之间数据最终一致性。在一次事务中,最多只能更改一个聚合的状态。如果一次业务操作涉及多个聚合状态的更改,应采用领域事件的方式异步修改相关的聚合,实现聚合之间的解耦(相关内容我会在领域事件部分详解)。

5. 通过应用层实现跨聚合的服务调用。为实现微服务内聚合之间的解耦,以及未来以聚合为单位的微服务组合和拆分,应避免跨聚合的领域服务调用和跨聚合的数据库表关联。

聚合、聚合根、实体、值对象特点总结

聚合的特点:高内聚、低耦合,它是领域模型中最底层的边界,可以作为拆分微服务的最小单位,但不建议对微服务过度拆分。但在对性能有极致要求的场景中,聚合可以独立作为一个微服务,以满足版本的高频发布和极致的弹性伸缩能力。

一个微服务可以包含多个聚合,聚合之间的边界是微服务内天然的逻辑边界。有了这个逻辑边界,在微服务架构演进时就可以以聚合为单位进行拆分和组合了,微服务的架构演进也就不再是一件难事了。

聚合根的特点:聚合根是实体,有实体的特点,具有全局唯一标识,有独立的生命周期。一个聚合只有一个聚合根,聚合根在聚合内对实体和值对象采用直接对象引用的方式进行组织和协调,聚合根与聚合根之间通过ID关联的方式实现聚合之间的协同。

实体的特点:有ID标识,通过ID判断相等性,ID在聚合内唯一即可。状态可变,它依附于聚合根,其生命周期由聚合根管理。实体一般会持久化,但与数据库持久化对象不一定是一对一的关系。实体可以引用聚合内的聚合根、实体和值对象。

值对象的特点:无ID,不可变,无生命周期,用完即扔。值对象之间通过属性值判断相等性。它的核心本质是值,是一组概念完整的属性组成的集合,用于描述实体的状态和特征。值对象尽量只引用值对象。

分享到: