编程思想 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 更适用于中大规模、产品化模式、业务可持续迭代、业务逻辑复杂度可预见的系统。

参考