编程思想 DDD
DDD,Domain Driven Design,领域驱动设计。
DDD 的概念有些抽象,尽力理解,见谅。
一、坏味道的来源
1. 表驱动开发
传统的开发流程往往是这样:先动手建立表结构,再在此基础上进行 CRUD,组装出功能点。
在这种模式下,开发以数据为起点,而非以业务为起点,这导致开发人员对业务没有整体概念。在开发过程中,为了满足需求,往往需要加表、加字段、加逻辑判断,久而久之,代码的坏味道浮现。
2. 臃肿的 Service 层
在 三层架构 + MVC
之下,所有逻辑都集中于 Service 层中,并且无法对其中的代码做规范和制约。随着系统功能迭代,Service 层中的逻辑势必越来越多,代码重复、代码耦合、长代码等问题都有可能出现,导致维护成本越来越高。
3. 贫血模型 + 面向过程
在 三层架构 + MVC
之下,对象只是数据的载体,逻辑书写在对象之外。
这种做法将对象的数据与操作分离,破坏了面向对象的特性,本质上是面向过程。在这种情况下,对象失去了对其动作的掌控,往往会导致其动作的重复、散落各地、代码意图不明。
二、什么是 DDD?
一句话解释 DDD:
将大系统拆分为若干模块,每个模块拥有 “自治权”;由对象维护自身的行为,Service 层只负责流程编排。
DDD,Domain Driven Design,领域驱动设计,它主要做的事情有:
- 通过事件风暴消除信息不对称,让业务相关人员都参与设计,确定每个业务领域的职责边界
- 以业务为起点,而不是以数据为起点,自顶(业务模型)向下地进行开发
- 将大的业务需求进行拆分,建立业务领域模型,分而治之
- 对象不再是贫血模型,而是会包含自身的行为;Service 层只负责流程编排,具体的行为通过调用方法实现
三、事件风暴
在传统的开发模式中,需求经过层层的转换才被转发至研发人员,这个过程常导致需求的偏离。
DDD 通过事件风暴消除信息不对称,让业务相关人员都参与设计,确定每个业务领域的职责边界。具体来说:任何与项目相关的业务人员、架构人员、研发人员都参与其中,通过头脑风暴的方式梳理需求。
四、战略设计与战术设计
1. 战略设计
战略设计从业务视角出发,建立业务领域模型,划分领域边界,建立通用语言的限界上下文。
具体来说,通过事件风暴来建立领域模型。这是一个从发散到收敛的过程。首先是发散,通常采用用例分析、场景分析、用户旅程分析,尽可能全面不遗漏地分析业务领域,并梳理领域模型之间的关系。其次是收敛,将发散过程中产生的实体、命令、事件等从不同的维度进行聚类,建立领域模型。
2. 战术设计
战术设计主要从技术视角出发,侧重于领域模型的技术实现,完成软件的开发与逻辑,包括:聚合根、实体、值对象、领域服务、资源库等代码逻辑的设计与实现。
五、基本概念
1. 领域和子域
在 DDD 中,任何边界明确的业务都能被成为领域。针对一个领域做二次划分,划分出的便是子域。
2. 限界上下文
限界上下文,即有边界的(限界)的上下文。
从限界上下文的结构上来说,它封装了通用语言及领域,对外提供了业务能力,是一个最基本的自治架构单元。
3. 通用语言
DDD 规定,在同一个限界上下文中,所有成员对于所有内容的认知应该是一致的。通过团队交流达成共识的,能够简单、清晰、准确描述业务的语言就是通用语言。
六、代码实现
1. 实体
实体描述具有生命周期的物体。对于这些物体而言,重要的不是其属性,而是其延续性。
在代码中,实体的表现类型是实体类,类中包含属性及方法,通过方法实现实体自身的业务逻辑。
2. 值对象
值对象描述不可变、没有生命周期的物体。从本质上来说,值对象是一个键值对集合,是一个由若干属性组成的整体。
在代码中,值对象的表现类型是实体类,实体类中仅包含若干属性。
在数据库中,值对象固定,因此可以不单独设表,而是作为 “值” 存储在实体表中。通过这种方式,可以减少数据库中表的数量,避免表与表之间复杂依赖,简化数据库设计,提升数据库性能。这是一种违反范式,可以提升数据库性能的做法。
值对象可以拆分属性嵌入,也可以序列化后嵌入,如下:
3. 聚合
聚合由若干个紧密关联的实体和值对象组合而成,它是领域的抽象体现,包含了领域内的一切事务。
4. 聚合根
聚合根也被称为根实体,它是聚合的管理者,负责逻辑处理。
首先,它作为实体本身,拥有实体的属性和行为,实现了自身的业务逻辑;其次,它作为聚合的管理者,在聚合内部负责协调实体和值对象按照固定的业务规则协同工作;最后,它还是聚合对外的接口人,负责接收外部的任务和请求。
5. 领域服务
当领域中的某个操作并不单独属于某个实体,而是需要多个实体协作完成时,应该将操作放在领域服务中。
6. 应用服务
应用服务可以看作是一个流程编排者,它本身不承担任何业务逻辑的具体处理,而是调用聚合根或领域服务中的方法。
7. 仓储
为了将领域模型持久化为数据库模型,DDD 提出了仓储的概念。仓储的定位是持久化和检索聚合,通过仓储,应用服务可以专注于逻辑编排,聚合根可以专注于逻辑处理,无需关心具体的持久化。
8. 事件模型
事件模型是为了不同领域之间的通信问题,如果不同领域之间需要通信,不应该直接调用,而是应该通过事件发送与监听的方式进行通信。
七、适用场景
DDD 存在以下几点弱势:
- 结构更加复杂
- 为了消除信息不对称,需要多方人员协作讨论,成本较大,对人员的要求较高
- 开发速度与迭代速度存在劣势
因此,DDD 更适用于中大规模、产品化模式、业务可持续迭代、业务逻辑复杂度可预见的系统。
参考
- DDD 实战课
- 深入浅出 DDD
- 领域驱动设计在互联网业务开发中的实践 - 美团技术团队
- 领域驱动建模(三):限界上下文 - 知乎