导图社区 DDD领域驱动设计与落地指南
这是一篇关于DDD领域驱动设计的思维导图,主要内容包括:基本概念,常用的分层架构,SPC业务场景DDD落地设计实现。DDD是一种软件设计的思想和方法论,其核心是将业务抽象成领域模型,并以领域为核心驱动力构建软件设计体系,将复杂的业务模型抽象化,非常适合微服务架构下的开发设计。
编辑于2025-04-28 09:15:53DDD领域驱动设计
基本概念
介绍
DDD是一种软件设计的思想和方法论,其核心是将业务抽象成领域模型,并以领域为核心驱动力构建软件设计体系,将复杂的业务模型抽象化,非常适合微服务架构下的开发设计。
具体指什么
以领域为核心驱动力构建软件设计体系,并围绕业务概念抽象出领域模型,通过领域和边界划分将复杂的业务模型抽象化、简单化,最终实现复杂软件应用系统的拆解和封装。
为什么学
DDD不仅可以用于微服务设计,还可以很好地应用于企业中台的设计,也适用于传统的单体应用。
1. DDD领域驱动设计与微服务的关系
微服务的兴起使得越来越多的开发人员开始关注如何将应用程序划分成一系列相互协作的微服务,并且每个微服务需要实现自己的业务逻辑。在这个过程中,开发人员发现他们需要一种更好的方式来划分业务领域边界,建立正确的业务模型和通用语言,以便更好地实现微服务的独立性和可维护性。 DDD正是一种能够帮助开发人员更好地设计和实现业务领域的模式。通过将业务领域作为核心,DDD可以帮助开发人员更好地理解业务需求,建立正确的业务模型和通用语言,从而更好地划分微服务边界,从而更好地实现微服务的独立性和可维护性。
使用DDD结合微服务,可以让系统更贴合业务需求,提升开发效率,增强系统的弹性和可维护性,帮助企业更快速响应市场变化。这种架构和设计理念特别适合复杂、变化快的业务场景。
2. 关注业务核心,提升业务价值
DDD强调以业务需求为核心,帮助开发团队深入理解业务领域,将复杂的业务逻辑划分到不同的领域模型中,从而确保软件更贴合实际业务。
3. 解耦复杂系统,增强模块化
微服务通过将系统拆分为多个独立、自治的服务,每个服务负责特定的业务能力,减少模块间的依赖,提高系统的可维护性和扩展性。
4. 团队协作优化
微服务架构允许不同团队独立开发、部署和维护各自的服务,配合DDD的领域边界定义,有助于团队专注于自己负责的业务领域,提升开发效率。
5. 技术多样性与技术演进
微服务支持不同服务使用不同的技术栈,根据业务需求选择最合适的技术方案,DDD提供明确的业务边界,确保技术多样性不会影响整体系统的稳定性。
6. 提升系统的弹性和可扩展性
微服务可以根据业务需求独立伸缩,DDD的领域模型帮助明确哪些部分需要高可用性和高性能,整体提升系统的弹性。
7. 应对复杂业务变化
业务需求不断变化,DDD帮助团队更好地应对变化,通过明确的领域边界和模型,减少变更带来的风险。微服务则允许逐步演进,避免整个系统重构。
8. 改善系统的可测试性和可部署性
微服务的独立性使得测试和部署变得更简单,DDD的聚焦领域模型也有助于编写更清晰、可维护的测试用例。
优缺点
优点
实现DDD的最大好处是第一时间将业务需求构建成领域模型,而不是将其切割为数据和行为,然后再用数据库去实现。
统一业务语言
通过使用统一的领域语言,准确传达业务规则,提升团队间的沟通效率。
清晰业务边界
统一各个子域的认识,通过领域模型界定需求实现边界。
提升变化应对
通过领域模型与数据模型的分离,将核心业务的不变与需求的变进行有效隔离,提升架构应变化的能力。
缺点
使用DDD需要一定的学习和使用成本,开发人员需要掌握一定的领域建模知识和技能。
在高并发场景下,由于DDD使用的模型可能会增加一些额外的开销。
精髓设计

战略设计
主要从业务视角出发,建立业务领域模型、划分领域边界、建立通用语言的限界上下文。在战略设计中,需要对领域进行全面的了解和分析,探究业务的规则和本质,以及领域未来的趋势发展和可能变化。
1. 建立业务领域模型
1. 统一语言
统一语言(Ubiquitous Language)是一种用于描述业务领域中的概念、规则和流程的语言。它是一种由领域专家、开发人员和用户共同理解的通用语言,而不是一种特定的编程语言或框架。 统一语言贯穿于整个开发过程,从需求分析到设计再到编码。它用于描述业务领域中的实体、值对象、聚合、领域服务等概念,同时也用于描述业务规则、流程和交互。在统一语言中,应该使用业务领域的术语和词汇,而不是技术术语和词汇。例如,应该使用“客户”而不是“用户”来描述应用程序的客户端,因为“客户”是业务领域中的术语,而“用户”则是技术领域的术语。
重要性
1. 帮助开发人员更好地理解业务需求和领域模型,同时也能帮助业务专家更好地理解技术和系统实现。
2. 提高代码的可读性和可维护性,因为代码中的术语和词汇能够被业务专家和开发人员共同理解。
2. 领域和子域
领域是业务领域的范围和边界。
在研究和解决业务问题时,DDD会按照一定的业务规则将业务领域进行细分,当领域细分到一定的程度后,DDD会将问题范围限定在特定的边界内,在这个边界内建立领域模型。简言之,领域是业务领域的范围和边界。
领域可以进一步划分为不同的子领域称为子域
每个子域都包含了一些特定的业务规则和功能,它们共同构成了整个业务领域。领域和子域是基于业务需求和功能进行划分,在划分领域和子域时,需要考虑到业务需求的变化和扩展,使得系统能够更好地适应业务需求的变化。
在实现时,每个子域都可以被划分成一个或多个限界上下文
每个限界上下文都包含了一些特定的业务规则和功能,它们共同实现了该子域的功能。
2. 划分领域边界
根据重要性的不同划分为
核心域
核心域是指业务领域中的核心业务逻辑和模型,它们是领域驱动设计的核心部分。核心域包含领域模型、领域服务、业务规则等,它们是业务领域中最重要、最稳定的部分。
通用域
通用域是指业务领域中通用的、可复用的业务逻辑和模型,它们不是核心业务逻辑,但可能在多个核心域中被使用。通用域包含一些通用的业务逻辑、数据校验、安全控制等。
支撑域
支撑域是指用于支持业务领域中的核心域和通用域的基础设施和工具,包括数据存储、消息传递、分布式系统、自动化测试等。支撑域中不包含业务逻辑和模型,而是聚焦于技术实现和系统监控、管理等方面。
3. 建立通用语言的限界上下文

限界上下文是指一个业务领域的边界,它包含了多个子域
每个子域都包含了一些特定的业务规则和功能。限界上下文用来封装通用语言和领域对象,提供上下文环境,保证在领域之内的一些术语、业务相关对象等(通用语言)有一个确切的含义,没有二义性。
限界上下文是一个显式的边界,领域模型便存在于这个边界之内
在边界内,通用语言中的所有术语和词组都有特定的含义,而模型需要准确地反映通用语言。通过限界上下文,开发人员可以更好的组织和管理领域中的不同部分,更好的理解业务需求和领域模型。
战术设计
从技术视角出发,侧重于领域模型的技术实现完成软件开发和落地,包括:实体、值对象、聚合、工厂、仓库、领域服务、领域事件等代码逻辑的设计和实现。战术设计关注的是领域中的具体情境和场景,需要针对具体的问题进行具体的分析和设计,以满足业务需求。
代表领域的概念
实体
概念
实体是拥有唯一标识和状态,且具有生命周期的业务对象。
使用
业务方面
在领域模型中实体是多个属性、操作或行为的载体。
实体的属性描述实体的状态比如客户的姓名、年龄等。
实体的行为指实体可以执行的操作,比如创建、更新等。
实体和值对象是组成领域模型的基础单元。
代码方面
在代码模型中,实体表现为实体类,这个类包含了实体的属性和方法,通过这些方法实现实体自身的业务逻辑。
运行方面
实体是以DO(领域对象)的形式存在,每个实体对象都有一个唯一的ID,这个ID在实体的整个生命周期内无论实体对象怎么变化都具有唯一性。
数据库持久化方面
DDD先构建领域模型,再针对业务场景构建实体对象和行为,最后将实体对象映射到数据持久化对象。
在领域模型映射到数据模型时,一个实体可能对应0个、1个或者多个数据库持久化对象。
一个实体对应0个数据库持久化对象,实体【运行化实体】不需要持久化到数据库中。 例如,临时计算结果或会话状态等。
public class CalculatedValue { public double Value { get; private set; } public CalculatedValue(double value) { Value = value; } // 业务逻辑方法 public void UpdateValue(double newValue) { Value = newValue; } } CalculatedValue不需要保存数据库
一个实体对应1个数据库持久化对象,【大多数情况下,实体会对应一个唯一的数据库记录。】
public class User { public Guid Id { get; protected set; } public string Username { get; set; } public string Email { get; set; } public User(Guid id, string username, string email) { Id = id; Username = username; Email = email; } // 业务逻辑方法 public void UpdateEmail(string newEmail) { Email = newEmail; } } 对应User表
一个实体对应多个数据库持久化对象,在一些复杂场景中,一个实体可能对应多个数据库持久化对象。例如,用户与角色之间的关系。
public class User { public Guid Id { get; protected set; } public string Username { get; set; } public ICollection<Role> Roles { get; set; } public User(Guid id, string username) { Id = id; Username = username; Roles = new List<Role>(); } // 业务逻辑方法 public void AddRole(Role role) { Roles.Add(role); } } public class Role { public Guid Id { get; protected set; } public string Name { get; set; } public Role(Guid id, string name) { Id = id; Name = name; } } 一个 User 实体可以对应多个 Role 实体。
与领域对象的区别

实体是领域对象的一种,通过唯一标识管理业务实体。
领域对象是更广泛的概念,包括实体、值对象等,共同构成业务模型。
领域对象(Domain Object)
泛指领域模型中的所有对象(包括实体、值对象等)
贫血模型 vs 充血模型
贫血模型:实体仅包含数据(如 Order 只有属性和 get/set),业务逻辑放在服务层。
所谓的贫血模型指的是定义领域对象的时候,只有对象的属性信息,没有对象的行为信息。贫血模型强调了业务逻辑和数据之间的分离,将业务逻辑和数据分别放在不同的对象中,导致领域模型变得贫血、不灵活和难以维护。
充血模型:实体封装业务逻辑(如 Order.Confirm()),符合面向对象设计。
充血模型指的是领域对象既包括属性信息,又包含行为信息。在充血模型中,领域对象包含了业务逻辑、数据和规则,它们被组织在一个聚合根中。相对而言贫血模型相对简单,模型上只有数据没有行为,针对简单的模型可以快速的完成交付,但是后期的成本较高,并且难以保证事务的一致性和完整性。充血模型则是领域模型模式,实现逻辑上由各自的对象负责,具备事务的一致性和完整性,事务边界也被包含在聚合根中。
领域对象与数据库表
实体可能映射到数据库表,但领域对象更关注业务含义,而非持久化细节。
聚合根与实体
聚合根是一种特殊的实体,负责管理聚合内其他对象的生命周期
值对象
概念
无标识性
值对象没有自己的唯一标识符。
它们的相等性是基于属性值的完全匹配。
不变性
一旦创建,值对象的状态就不能被改变。
所有的修改操作实际上都是创建一个新的实例来替换旧的。
基于值的比较
判断两个值对象是否相同,只需要比较它们包含的所有属性值是否逐一相等。
内聚性
将相关的属性组合在一起形成一个有意义的整体概念。
这种封装有助于提高代码的可读性和可维护性。
生命周期依附于其他实体
值对象自身没有独立的生命周期,它们的存在往往依赖于某个具体的实体对象。
当关联的实体被销毁时,相关的值对象通常也会随之失效。
使用
业务方面
值对象是若干不可修改的属性集合,只有数据初始化操作和有限不涉及数据修改的行为,基本不包含业务逻辑。值对象在逻辑上依然是实体属性的一部分
代码方面
如果值对象是单一属性例如字符串、整型、枚举等,则直接定义为实体类的属性;如果值对象是属性集合,则把它设计为Class类,包含多个属性。
运行方面
值对象嵌入到实体中,有两种不同的数据格式
属性嵌入的方式
通常适用于值对象的属性较为简单,且可以直接映射到数据库表的列上
在实体中嵌入这个值对象时,可以直接将其属性作为实体的属性,进而持久化到数据库中
序列化大对象的方式
当值对象的属性较为复杂,或者值对象本身包含嵌套的值对象时,直接嵌入属性可能会变得复杂和难以管理。这时,可以将值对象序列化为一个字符串或二进制大对象(BLOB),并存储在实体的一个字段中。
常见的序列化格式包括JSON、XML等。
将这个值对象序列化为JSON字符串,并存储在实体的一个字段,【映射为一个TEXT或VARCHAR类型的列】
public class Address { private String street; private String city; private String zipCode; private Country country; // 假设Country也是一个值对象 // getters and setters } public class User { private Long id; private String name; private String addressJson; // 存储序列化后的Address对象 // getters and setters }
领域服务
概念
领域服务(Domain Service)是指跨越多个聚合或子域的逻辑服务,用于处理业务需求和行为。领域服务通常用于执行跨聚合的操作,例如处理业务规则、集成外部系统、执行复杂业务流程等。
实体和领域服务在实现业务逻辑上不是同级的,当领域中的某些功能,单一实体或者值对象不能实现时,领域服务就会出马,它可以组合聚合内的多个实体或者值对象,实现复杂的业务逻辑。
使用
无状态:领域服务通常是无状态的,它们不维护自己的状态,而是依赖于传入的参数来执行操作。
跨实体操作:一些逻辑不适合放在单一实体内部时,领域服务可以处理跨越多个实体或值对象的业务逻辑。
协调作用:领域服务可以协调多个实体或值对象之间的交互,以实现更复杂的业务流程。
业务逻辑:领域服务封装了那些不属于任何单一实体但又属于整个业务领域的逻辑,例如支付处理、用户认证等。
与实体的区别
实体
适合封装与自身状态紧密相关的业务逻辑。
领域服务
适合封装跨越多个实体或值对象的复杂业务逻辑,提供协调和组合功能。
实例
订单实体
负责管理订单的状态(如待支付、已支付、已发货等)和基本操作(如添加商品、更新数量等)。
支付服务
负责处理支付相关的逻辑,包括与外部支付网关的交互、验证支付信息、更新订单状态等。支付服务不是一个实体,其通过协调多个实体(如订单、用户、支付方式)来完成支付这一业务流程。
领域事件

领域事件(Domain Event)是指业务领域中发生的一些事件,通常意味着领域对象状态的改变。
领域事件在系统中起到了传递消息、触发其他动作的作用,是解耦领域模型的重要手段之一。
使用
领域事件可以由领域模型中的实体或服务触发,例如当用户注册成功时,可以触发一个“用户注册成功”的领域事件。
领域事件也可以由外部系统或服务触发,例如当第三方支付系统支付成功时,可以触发一个“订单支付成功”的领域事件。
领域事件处理包括
事件构建和发布
事件基本属性通常包含事件的标识符、时间戳和事件数据等信息;业务属性用于记录事件发生时刻的业务数据。事件的基本属性和业务属性一起构成事件实体。
事件发布方式很多,可以通过应用服务或领域服务发布到事件总线或消息中间件;也可以从事件列表中定时获取增量事件数据发布到消息中间件。
事件数据持久化
用于系统之间的数据对账,或者实现发布方和订阅方数据的审计
持久化到本地业务数据库的事件表中,利用本地事务保证业务和事件数据的一致性
持久化到共享的事件数据库中,事件数据库和业务库是独立的。
事件总线
实现微服务内聚合之间领域事务,提供事务分发和接收等服务
消息中间件
跨微服务的领域事件大多会用到消息中间件,实现跨微服务的事件发布和订阅
事件接收和处理
订阅方采用监听机制接收消息队列中的事件数据,当完成事件数据的持久化处理后,就可以进行下一步的业务处理
用于管理对象的生命周期
聚合
聚合、聚合根与实体对象值对象的关系
DDD中实体和值对象是很基础的领域对象,实体一般对应业务对象、值对象一般是属性集合,但是实体和值对象都是个体化的表现。而聚合是把一些关联性极强、生命周期一致的实体、值对象放到一个聚合里。聚合根(Aggregate Root)是指聚合中的实体,它负责聚合的内聚性和一致性,同时也是聚合的入口点。
聚合和聚合根
聚合有一个聚合根和上下文边界,这个边界根据业务单一职责和高内聚原则,定义了聚合内部应该包含哪些实体和值对象,而聚合之间的边界是松耦合的。按照这种方式设计出来的微服务很自然就是“高内聚、低耦合”的。 
在领域驱动设计中,聚合设计是非常重要的一部分,设计的原则
1. 保持完整性
定义:聚合是一组具有业务意义的相关对象的集合,它们被视为数据修改和事务处理的基本单元。
重要性:明确的边界有助于维护聚合内部数据的一致性和完整性,防止外部随意修改。
2. 保持一致性
强一致性:在聚合内部,所有操作都应该保证数据的即时一致性。
最终一致性:不同聚合之间可以允许有一定的延迟,在事务完成后达到一致状态。
业务规则约束:聚合内的所有元素都必须遵守共同的业务规则和约束条件。
3. 保持小型聚合
优势
更容易理解和维护。
减少变更的影响范围,便于进行局部优化和重构。
提高系统的并发处理能力。
4. 避免聚合根以外的实体
聚合根的角色:是聚合的入口点和唯一访问点,负责维护聚合的内部状态和边界。
问题:非聚合根实体的直接操作可能导致数据的不一致和混乱。
解决方案:尽量通过聚合根来操作和管理其内部的实体和值对象。
5. 避免跨聚合引用
直接引用的弊端
增加了聚合之间的耦合度。
破坏了聚合的封装性和独立性。
可能导致难以追踪的数据一致性问题。
推荐做法:使用外部聚合根ID进行引用,通过领域事件或应用服务来协调不同聚合之间的交互。
6. 明确职责:每个聚合应有明确的业务职责和边界
7. 稳定边界:聚合的边界应该尽量保持稳定,避免频繁变动。
8. 合理划分:根据业务需求和领域模型合理划分聚合,既要保证业务功能的完整性,又要考虑系统的性能和可维护性。
工厂
概念
如果实体或值对象的创建过程非常复杂,可以将其委托给工厂。
DDD中工厂是指用于创建领域模型对象的工厂方法,它用于将业务逻辑和实现技术解耦。
它是一种设计模式,允许开发人员通过调用工厂方法来创建对象,而不需要直接在代码中实例化对象。
使用
职责分离
创建复杂对象或聚合的职责被委托给一个专门的工厂对象。
这个工厂对象本身不属于领域模型中的核心实体或值对象,但它确实是领域设计不可或缺的一部分。
封装复杂性
工厂提供了一个清晰定义的接口来创建所需的对象。
客户端代码不需要了解对象创建的具体步骤和内部细节,只需要通过工厂的接口进行交互。
解耦客户端与具体实现
工厂模式使得客户端代码与实际被创建的对象类型解耦。
这增加了系统的灵活性,因为可以在不修改客户端代码的情况下更改对象的创建逻辑或替换具体的产品类。
聚合的完整性保障
当涉及到聚合根及其组成部分时,工厂负责一次性地构建整个聚合。
确保所有相关的业务规则和不变条件都得到遵守和维护。
仓库

概念
仓库(Repository)是一种模式,用于封装数据访问逻辑,提供对数据的持久化和查询。
仓库操作的最小单元就是聚合,每个聚合会对应一个仓库。它旨在将数据访问细节与领域模型分离,使领域模型更加独立和可测试。
仓库提供了一种统一的接口,使得领域模型可以与不同的数据存储方式进行交互,同时也提供了一些查询操作。
使用
抽象数据访问
仓库隐藏了底层数据存储的具体实现细节。
开发人员无需关心数据是存储在关系型数据库、NoSQL数据库、文件系统还是其他介质中。
统一接口
定义了一组标准的操作方法,如Add、Update、Delete、GetById、Query等。
这些方法使得领域模型的使用变得简单且一致。
业务逻辑集成
在仓库的方法内部,除了基本的CRUD操作外,还可以嵌入额外的业务规则和验证逻辑。
这有助于确保数据的完整性和一致性。
解耦
将数据访问代码与业务逻辑分离,增强了系统的模块化和可测试性。
方便更换存储方案而不影响到上层应用。
优点
提高可维护性:当数据访问逻辑发生变化时,只需修改仓库的实现,而不影响业务逻辑层。
增强测试性:可以使用Mock对象轻松地对仓库进行单元测试。
促进团队协作:前端开发人员可以与后端开发人员并行工作,因为他们都依赖于相同的抽象接口。
用于集成或跟踪
领域事件
事件溯源
常用的分层架构

单机架构
采用面向过程的设计方法,系统包括应用客户端层和数据库层,整个系统围绕数据库驱动设计和开发。
集中式架构
采用面向对象的设计方法,经典的三层架构包括业务接入层、业务逻辑层和数据访问层。这种集中式架构容易在某一层变得臃肿,另外受限于集中式的限制,可扩展性差,架构上不灵活。
分布式微服务架构
微服务架构将应用程序拆分为一系列相互协作的微服务,每个微服务负责一个特定的业务领域或业务功能,从而实现应用之间的解耦,同时解决单体应用扩展性和灵活性问题。在微服务架构中,每个微服务都是独立的,可以使用不同的编程语言、数据存储和框架,这使得每个微服务可以独立地演进和部署。
DDD领域驱动设计中架构模型
分层架构

将领域模型和业务逻辑分离出来,并减少对基础设施和应用层的依赖,从最早三层架构,演化到四层架构、五层架构,包括用户接口层、应用层、领域层和基础设施层。分层架构中,每层只能与位于其下方的层耦合。
六边形架构

也称为端口与适配器。对于每种外界类型,都有一个适配器与之相对应。该架构的定义涉及六个部分,分别是应用程序、域、基础设施、适配器、API和外部系统。六边形架构的核心理念是应用通过端口与外部进行交互。
CQRS架构

CQRS架构是一种读写分离的架构设计,系统被分为两个部分:命令端(Command)和查询端(Query)。命令端负责处理写操作(例如修改数据),而查询端则负责处理读操作(例如查询数据)。
事件驱动架构
用于处理事件的生成、发现和处理等任务的软件架构。在这种架构中,领域对象之间通过发布和订阅事件来通信,一个事件通常会引发多个服务的同步,从而引起事件的扩散。
SPC业务场景DDD落地设计实现
实施步骤
1. 业务场景分析

事件风暴法、四色建模法、用例分析法
分析业务中的事件,寻找业务逻辑和业务规则
业务场景的分析讨论
参与角色包括领域专家【业务专家】、产品经理、需求分析人员、架构师、项目经理、开发经理和测试经理。
尽可能遍历所有业务细节,列出具体的业务场景、业务规则、业务之间的关联以及热点问题等。
业务讨论与梳理
经过事件风暴讨论后,总结出SPC中典型的业务场景
档案建立
站点
档案名称、字段、原因
项目
SPEC
断点模式
控制图
判异规则
OCAP
数据采集
PSN处理
报表查询
……
针对分析出的业务流程按照业务操作对象、事件和命令(动作)形成事件风暴小黑板对整个业务流程进行梳理。
2. 领域建模

根据场景分析过程中产生的领域对象,找出产生命令的实体,分析实体之间的依赖关系产生聚合,为聚合划定界限上下文,建立领域模型以及模型之间的依赖。
根据业务场景分析,提取产生这些行为的实体个体,包括档案、用户、PSN、OCAP、SPEC、数据采集、报表、系统管理……。
完成领域对象分析后,需要构建业务聚合,首先在实体中找到聚合根,根据聚合根的特点,聚合根是一个实体具有全局唯一标识,并且具备生命周期的同时需要专门的模块进行管理。根据这些特征就有了档案、用户、PSN、OCAP、SPEC、报表、系统管理……这些模块聚合。
3. 划分边界上下文

获得了整个业务流程中的所有聚合后,需要更具业务语义上下文将具体的聚合划分到对应的上下文中。
一个限界上下文拆分为一个微服务,微服务的设计还需要考虑服务的粒度、分层、边界划分、依赖关系和集成关系。
4. 后续
对领域进行CQRS架构或其他架构混合分层设计。