架构演进之 DDD:从 CRUD 到领域驱动设计

46 阅读5分钟

大家好,我是G探险者!

今天来聊一聊DDD。

在软件开发的世界里,架构从来不是一开始就复杂的。

绝大多数系统,最初都只是简单的 CRUD 系统。但随着业务的增长,系统复杂度不断增加,原有的开发模式逐渐暴露出问题。于是,新的架构思想开始出现。

DDD(Domain-Driven Design,领域驱动设计)正是在这样的背景下诞生的。

理解 DDD 的最好方式,并不是直接学习那些复杂的概念,而是从软件架构的演进过程来看:

为什么我们最终需要 DDD。


一、第一阶段:CRUD时代 —— 数据库驱动开发

在企业软件早期,大多数系统都是围绕数据库构建的。

典型架构结构:

Controller → Service → DAO → Database

系统本质上是:

页面 ++ SQL

开发流程通常是:

  1. 设计数据库表
  2. 编写 SQL
  3. 提供接口
  4. 页面展示数据

这种模式的核心特点是:

  • 数据库是系统中心
  • 业务逻辑很薄
  • 系统主要是增删改查

这种开发方式就是我们熟悉的:

CRUD(Create Read Update Delete)模式

这种模式非常适合:

  • 管理后台
  • CMS系统
  • 配置系统
  • 权限系统

直到今天,大量企业系统仍然采用这种模式。

但当业务复杂度上升时,问题开始出现。


二、第二阶段:三层架构 —— 软件工程化

随着系统规模扩大,软件工程开始强调结构化设计,于是出现了经典的 三层架构

表现层(Controller)
业务层(Service)
数据层(DAO)

职责划分:

职责
Controller接收请求
Service业务逻辑
DAO数据访问

这种架构带来的改进:

  • 代码结构更清晰
  • 业务逻辑与数据访问分离
  • 更利于团队协作

三层架构成为 Java 企业开发的主流模式。

但随着系统规模继续扩大,一个新的问题逐渐出现。


三、第三阶段:Service 膨胀

在三层架构中:

  • Controller 很薄
  • DAO 只负责数据库

于是,所有业务逻辑都堆到了:

Service层

随着业务增加,Service层逐渐变成:

状态判断
流程控制
业务规则
外部系统调用
事务控制
日志处理
异常处理

最终形成所谓的:

Fat Service(肥胖服务层)

一个 Service 方法可能:

  • 几百行代码
  • 大量 if-else
  • 多个系统调用

Service逐渐变成:

业务逻辑的大泥球


四、第四阶段:贫血模型

与此同时,系统中的领域对象往往非常简单。

例如一个订单对象:

class Order {
    Long id;
    Integer status;
    BigDecimal amount;
}

它通常只有:

  • getter
  • setter

而业务逻辑却写在 Service:

orderService.pay(order)
orderService.cancel(order)
orderService.refund(order)

这种模式被称为:

贫血模型(Anemic Domain Model)

问题是:

  • 对象只有数据,没有行为
  • 业务规则散落在各个 Service
  • 代码难以理解
  • 系统难以维护

随着系统复杂度增加,这种问题会越来越严重。


五、第五阶段:业务复杂度爆炸

当系统进入核心业务阶段时,复杂度会快速增长。

例如一个订单系统,可能涉及:

  • 下单
  • 支付
  • 发货
  • 退款
  • 售后
  • 库存锁定
  • 营销活动
  • 价格计算

各种业务规则:

已支付订单不能取消
库存不足不能下单
优惠券规则
会员等级规则
退款时间限制

如果这些规则散落在:

  • Service
  • SQL
  • Controller

系统很快会变得:

  • 难以理解
  • 难以维护
  • 难以扩展

这时开发者往往会发现:

功能可以做,但系统越来越难改。


六、DDD的出现:业务驱动架构

在这样的背景下,Eric Evans 在 2003 年提出了:

Domain-Driven Design(领域驱动设计)

DDD的核心思想是:

软件设计应该围绕业务领域进行,而不是围绕技术实现。

传统架构关注:

数据库
接口
框架

DDD关注:

业务模型
领域概念
业务规则
系统边界

简单来说:

传统架构DDD
技术驱动业务驱动
数据库中心领域模型中心

七、DDD核心思想

DDD包含几个关键概念。

1 领域模型

系统应该围绕业务模型设计。

例如订单系统核心对象:

订单 Order
订单项 OrderItem
支付 Payment
优惠 Coupon

领域对象不仅有数据,还应该有行为:

order.cancel()
order.pay()
order.refund()

业务规则应该由领域对象负责。


2 统一语言(Ubiquitous Language)

DDD强调:

开发人员和业务人员应该使用统一的业务语言。

例如:

  • 订单
  • 库存锁定
  • 支付确认
  • 退款申请

这些概念应该直接体现在代码中。

这样代码就能表达业务。


3 限界上下文(Bounded Context)

在复杂系统中,同一个概念在不同系统中可能含义不同。

例如“订单”:

系统含义
交易系统用户购买记录
仓储系统发货任务
财务系统结算凭证

DDD提出:

每个业务上下文拥有自己的模型

这就是:

Bounded Context(限界上下文)


八、DDD与微服务

随着微服务架构的发展,人们发现:

DDD提供了一种非常自然的服务拆分方式。

因为:

Bounded Context ≈ Microservice

例如:

订单上下文 → Order Service
库存上下文 → Inventory Service
支付上下文 → Payment Service

DDD帮助架构师回答一个关键问题:

服务应该如何拆分?


九、DDD的真正价值

很多人认为 DDD 只是:

  • 多几个类
  • 多几层结构

其实并不是。

DDD真正解决的是:

复杂业务系统如何组织

它帮助架构师:

  • 理解业务
  • 划分系统边界
  • 管理业务复杂度
  • 支持系统长期演进

十、架构演进时间线

从架构演进角度看,软件开发经历了这样一条路径:

CRUD开发
      ↓
三层架构
      ↓
Service膨胀
      ↓
贫血模型
      ↓
业务复杂度爆炸
      ↓
DDD出现
      ↓
领域建模
      ↓
Bounded Context
      ↓
微服务架构

DDD并不是为了让系统变复杂,而是为了:

用更好的模型管理复杂业务。


十一、结语

从架构演进的角度来看,DDD的意义在于:

让软件系统真正反映业务世界。

当系统结构与业务结构一致时,软件才能具备:

  • 可理解性
  • 可维护性
  • 可演进性

这正是领域驱动设计的核心价值。