12-深层模型与重构-DDD领域驱动设计


title: "12 深层模型与重构"


学习目标

  • 理解"浅层模型 vs 深层模型"的区别:浅层是"数据结构+CRUD",深层是"业务概念+规则"
  • 能识别"隐含概念(Implicit Concept)",并通过重构显性化
  • 掌握从浅层模型到深层模型的演进路径:识别 → 提取 → 重构 → 验证

核心概念

浅层模型(Anemic Model)是什么

浅层模型的特征:

  • 实体只有 getter/setter,没有业务行为
  • 业务规则散落在 Service/Controller/SQL 中
  • 模型只是"数据的容器",不表达业务意图

典型症状:

text 复制代码
class Order {                    // 浅层:只有数据
  String status
  List<OrderItem> items
  // 没有 pay()、cancel()、addItem() 等业务方法
}

class OrderService {             // 规则散落在这里
  void pay(Order order) {
    if (order.status == "CREATED" && order.items.size > 0) {
      order.status = "PAID"      // 直接改字段
    }
  }
}

深层模型(Rich Model)是什么

深层模型的特征:

  • 实体/值对象包含业务行为
  • 业务规则封装在模型内部
  • 模型用领域术语表达,领域专家能理解

本书示例的"订单模型"就是深层模型的示例:

  • pay()cancel()addItem() 等方法封装业务规则
  • OrderStatus 状态机显性化状态转换约束
  • OrderPaymentSpecification 显性化"可支付"规则

隐含概念(Implicit Concept)是什么

隐含概念是"业务讨论中存在,但代码中没有显性表达"的概念。例如:

  • 业务说"订单可支付",代码里是 if (status == "CREATED" && ...)
  • 业务说"订单行数限制",代码里是 if (items.size() >= 50)
  • 业务说"订单状态转换",代码里是 order.status = "PAID"

重构的目标是:把这些隐含概念提取成显性的领域对象(规约、值对象、状态机等)。

课堂讲授:从浅层到深层的重构路径

步骤 1:识别隐含概念

通过以下信号识别:

  • 业务讨论中的名词:例如"可支付条件""订单行数限制"
  • 散落的 if/else:同样的判断逻辑在多处重复
  • 魔法数字/字符串 :例如 50"CREATED""PAID"

步骤 2:提取并显性化

把隐含概念提取成:

  • 值对象 :例如 OrderStatus(状态机)、Money(金额+币种)
  • 规约 :例如 OrderPaymentSpecification(可支付规则)
  • 常量/枚举 :例如 MAX_ORDER_LINES = 50

步骤 3:重构模型行为

把散落的规则移回模型:

  • OrderService.pay() 移到 Order.pay()
  • if/else 移到规约/状态机方法

步骤 4:验证与迭代

通过测试验证重构后的模型:

  • 业务规则是否仍然正确
  • 代码是否更易读、更易维护

伪代码示例:从浅层到深层的重构

重构前(浅层)

text 复制代码
class Order {
  String status
  List<OrderItem> items
}

class OrderService {
  void pay(Order order) {
    if (order.status.equals("CREATED") 
        && order.items.size() > 0 
        && order.totalAmount > 0) {
      order.status = "PAID"
      // 发布事件...
    }
  }
}

重构后(深层)

text 复制代码
class Order {
  OrderStatus status
  List<OrderItem> items
  
  pay():
    require OrderPaymentSpecification().isSatisfiedBy(this)
    status = status.transitionToPaid()
    domainEvents.add(OrderPaidEvent(...))
}

class OrderPaymentSpecification {
  isSatisfiedBy(order):
    return order.status.canPay()
       and order.totalAmount > 0
       and order.items not empty
}

class OrderStatus {
  canPay(): value == CREATED
  transitionToPaid(): require value == CREATED; return OrderStatus(PAID)
}

重构策略:何时重构、如何重构

何时重构

  • 发现重复的业务规则:同样的判断在多处出现
  • 业务规则变化频繁:规则散落导致修改困难
  • 领域专家看不懂代码:代码与技术细节耦合,业务意图不清晰

如何重构(小步快跑)

  1. 先提取值对象/规约:把魔法值/散落规则提取出来
  2. 再移回模型行为:把 Service 里的规则移到聚合/值对象
  3. 最后优化表达:用领域术语命名,让代码"说业务话"

常见误区

  • 误区 1:一次性大重构:风险高、容易出错;应该小步快跑,每次重构后测试通过
  • 误区 2:过度设计:为了"深层"而深层,引入不必要的抽象
  • 误区 3:忽略业务价值:重构应该让业务规则更清晰、更易维护,而不是"看起来高级"
  • 误区 4:重构后不更新测试:测试应该同步演进,验证重构后的模型

课堂练习

  1. 以"订单模型"为例,请列出 3 个"从浅层到深层"的重构点(例如:用状态机显性化状态转换、用规约显性化规则、提取常量/值对象等)。
  2. 假设你看到一个 OrderService 里有这样的代码:
text 复制代码
if (order.status == "PAID" && order.shippingAddress != null) {
  order.status = "SHIPPED"
}

请设计一个重构方案,把这段逻辑移到深层模型(提示:考虑状态机与规约)。

  1. 给出一个你认为"隐含概念"的例子(来自你的项目或想象),并说明如何显性化。

自测题

  1. 浅层模型与深层模型的关键区别是什么?
  2. 如何识别"隐含概念"?
  3. 为什么说"重构应该小步快跑"?

延伸阅读

相关推荐
weixin_440730501 天前
java面向对象OPP-三大特性
java·开发语言·python
蕨蕨学AI1 天前
【Wolfram语言】37 布局与显示
开发语言·wolfram
No芒柠Exception1 天前
Spring Boot 实现分片上传、断点续传与进度条
java·后端
Godspeed Zhao1 天前
现代智能车机系统4——HPC(1)
架构·汽车
k***92161 天前
如何在C++的STL中巧妙运用std::find实现高效查找
java·数据结构·c++
m0_502724951 天前
在Qt中激活已运行的应用程序实例
开发语言·qt
君爱学习1 天前
Spring AI简介
java
沐知全栈开发1 天前
Kotlin 对象表达式/声明
开发语言
EnzoRay1 天前
注解
java