领域驱动设计
DDD 是什么
传统的 MVC
MVC 是模型(Model)、视图(View)、控制器(Controller)的简写,其核心思想是通过将业务逻辑、数据、显示分离来组织代码,其中 Model 是数据库模型,View 层负责视图展示,而业务逻辑在 Controller 中实现(一般由众多 Service 来辅助实现)。

对于小型,且业务逻辑不复杂的系统,MVC 简单高效,不失为一个合适的设计架构。但是随着业务迭代,逻辑膨胀,MVC会开始力不从心,主要有几个原因:
- MVC 是完全面向技术的架构设计,分层的系统设计方便了开发者,但是完全没有考虑业务,一股脑把所有业务逻辑放到了 Service 层
- MVC模式天然切割了数据和行为,然后用数据库实现数据,用服务实现行为,容易造成需求的首尾分离
- 缺乏明确的边界划分,至少在顶层设计层面没有边界划分的规范要求,更多地是靠技术负责人根据经验进行划分,大规模团队协作容易出现职责不清晰、分工不明确的问题
DDD 定义
DDD 全称 Domain-Driven-Design,领域驱动设计,是一种模型驱动设计的方法,通过领域模型捕捉领域知识,使用领域模型构造更易维护的软件。模型在领域驱动设计中,有三个重要用途:
- DDD 通过对业务需求的分析和领域模型设计,划分出领域、子领域、限界上下文,指导了系统的架构设计,直接反映软件实现的结构
- DDD 聚焦于领域模型,降低了业务和技术的耦合度,以模型为基础形成团队的统一语言,将复杂的问题分而治之,指导团队成员分工协作
- 领域模型设计使得模型与业务的真实世界保持一致,促使业务知识通过模型得以传递和沉淀
基础概念
在DDD中分为战略设计和战术设计:
- 战略设计主要面向业务,进行分析拆解,为业务系统的设计打下基础
- 战术设计更多的是面向技术,在战略设计的基础上,具体设计系统实现
战略设计
战略设计指的是对整个领域进行分析和规划,确定领域中的概念、业务规则和领域边界等基础性问题。在战略设计中,需要对领域进行全面的了解、分析、拆解,探究业务的规则和本质,并且需要考虑到领域的未来发展趋势和可能的变化。
领域
领域,Domain,是一个团队所要做的业务全集,这是一个面向业务的概念,处在某个业务团队里,你们要做的事就是这个团队的领域。
子域
子域,Sub Domain,是在领域这个整体下,划分出来的业务子领域,每个子领域有各自的业务概念、规则、流程,这些子域互相独立,但又相互关联。
根据在领域中的重要程度,分为:
- 核心域:决定业务核心竞争力的子域,如业务交易、订单
- 通用域:具有通用功能,被多个子域使用,如登录、权限
- 支撑域:支撑其它领域,具有业务特性,但又不通用,如某某业务的基础数据
限界上下文
Bounded Context,是业务边界的划分,可以是一个子域或多个子域的集合,通常划分的依据是:一个限界上下文必须支持一个完整的业务流程,保证这个业务流程所涉及的领域都在一个限界上下文中。例如业务的售前流程,对应的限界上下文要能支持用户完整地浏览、下单。
限界上下文一般是微服务拆分的依据,即每个限界上下文对应一个微服务。
战术设计
战术设计则是在战略设计的基础上,对领域中的具体问题进行具体的解决方案设计。关注的是领域中的具体情境和场景,需要针对具体的问题进行具体的分析和设计,以满足业务需求。
实体
实体是拥有唯一标识和状态,且具有生命周期的业务对象,通常代表现实世界中的某个概念,可以是名词,也可以是动作。
根据数据和业务逻辑在对象中封装程度的不同,实体有四种类型:
- 失血模型:仅包含属性,连基本的 getter/setter 都没有,或者完全由框架动态生成,所有操作都通过外部工具类、DAO 或反射完成,如某些 ORM 的代理对象
- 贫血模型:数据和行为分离,模型仅包含属性和简单的 getter/setter,几乎没有业务逻辑,业务逻辑都通过领域服务(Service)来完成,如 Java 中的 POJO
- 充血模型:领域对象不仅包含数据,还封装了与自身相关的业务逻辑和行为,能主动响应业务操作
- 胀血模型:领域对象承担了过多职责,不仅包含自身业务逻辑,还混入不属于它自身的业务逻辑、跨领域逻辑,甚至基础设施逻辑。
DDD 推荐使用充血模型,实际开发也常用贫血模型+复杂的领域服务来构建业务逻辑。失血模型一般只有和底层 DB 进行映射才会使用,而涨血模型是一种 anti-pattern,会导致对象臃肿、难以测试、违反单一职责原则 (SRP)。
值对象
通过对象属性值来识别的对象,它将多个相关属性组合为一个概念整体,用来描述领域的特定方面,值对象没有唯一标识,没有生命周期,不可修改,当值对象发生改变时只能替换。例如字符串、整型、枚举等。
聚合
聚合是一组领域对象的集合,作为一个整体被看作是一个事务边界,通常包含一个或多个实体和值对象。聚合内的所有对象要么全部成功保存,要么全部不保存,以此来保证数据的一致性。
聚合根,是聚合中的一个特定实体,它是外部对象唯一可以持有的引用。聚合外部的对象只能通过聚合根来访问或修改聚合内部的状态和对象。

聚合根可以理解成聚合根是若干对象的管理器,统筹这些对象所反映的业务实体。举个例子,订单通常可以作为一个聚合根,它聚合了下单的用户、订单价格、订单内容等实体,订单决定了整个聚合的状态和行为,并且是外部对象与聚合交互的唯一途径。
领域、子域、限界上下文、聚合都是用来表示一个业务范围,领域、子域、限界上下文属于战略设计,而聚合属于战术设计,聚合的范围是小于前三者的。
领域服务
有些领域中的动作看上去并不属于任何对象。它们代表了领域中的一个重要的行为,不能忽略它们或者简单地把它们合并到某个实体或者值对象中。当这样的行为从领域中被识别出来时,推荐的实践方式是将它声明成一个服务,这个服务就是领域服务。例如业务系统常用的各种 Service。
领域事件
领域事件是发生在领域中且值得注意的事件。而领域事件通常意味着领域对象状态的改变。领域事件在系统中起到了传递消息、触发其他动作的作用,是解耦领域模型的重要手段之一。我们往往利用消息队列来传递领域事件。
工厂和资源库
DDD 还涉及了两种设计模式:
- 工厂模式:将创建复杂对象和聚合的职责分配给一个单独的对象,该对象本身并不承担领域模型中的职责,但是依然是领域设计的一部分。
- 资源库模式:用于封装数据访问逻辑,将底层数据存储进行抽象,提供对数据的持久化和查询。旨在将数据访问细节与领域模型分离,使领域模型更加独立和可测试,例如 MyBatis 的 Mapper。
领域建模
事件风暴建模
四色建模法
总结
DDD 是一种软件架构设计方法,直接面向业务的软件工程指导思想,究其本质,还是为了应对软件的高复杂度,实现高内聚、低耦合的一个工具,而并非银弹。因此在实战中需要结合实际业务理解学习其思想。