Drill/常见大型软件架构设计快速入门101

DDD

## DDD 是什么:把“业务复杂度”放回软件设计中心

**DDD(Domain-Driven Design,领域驱动设计)**不是某种框架或微服务模板,而是一套在**复杂业务**下更容易保持“可理解、可演进”的建模与架构方法。它的核心思想是:
> 软件的结构应当围绕“领域(业务)”来组织,而不是围绕技术层(Controller/Service/DAO)或数据库表来组织。

在你提供的文章语境里,DDD 被放在“可维护性”“清晰边界与稳定契约”“为未来拆分铺路”这些目标之下,因此它更像一种**划分模块边界、控制复杂度、降低协作摩擦**的手段。

---

## DDD 解决的典型痛点

在大型系统里,最难的是“长期变化”。DDD 常用来对抗这些问题:

- **业务语言不统一**:产品说“订单关闭”,研发说“状态=9”,测试说“取消”,最后代码里出现三套概念。
- **模型失真**:代码结构跟业务不一致,新增功能只能“到处 if/else”。
- **边界模糊**:一个需求要改 10 个模块,且彼此互相直接引用数据库或内部类。
- **一致性失控**:为了一个功能跨表跨服务强事务,导致性能和可用性风险。

---

## 两大支柱:战略设计 vs 战术设计

### 1) 战略设计(Strategic Design):先把边界划清楚
战略设计关心“系统怎么拆、团队怎么协作、模型在哪些边界内成立”。

- **限界上下文(Bounded Context)**
同一个词在不同上下文含义可能不同,DDD 允许它们并存,但要求**边界内自洽**、边界间通过契约连接。
例:
- 在“交易上下文”里,`Order` 关注支付、履约状态
- 在“客服上下文”里,`Order` 关注投诉、补偿、工单关联
两边都叫 Order,但字段、规则、生命周期可能完全不同。

- **上下文映射(Context Mapping)**:上下文之间怎么集成
常见方式包括:API 调用、事件订阅、共享内核(谨慎)、防腐层(ACL)等。
其中 **防腐层(Anti-Corruption Layer)** 很关键:用一层适配把外部系统的“脏模型”隔离开,避免污染自己的领域模型。

### 2) 战术设计(Tactical Design):在边界内把模型做好
战术设计关心“代码怎么写更贴近业务模型”。

- **实体(Entity)**:有身份(id),生命周期中属性会变
- **值对象(Value Object)**:无身份、不可变,靠值相等(例如 Money、Address)
- **聚合(Aggregate)与聚合根(Aggregate Root)**:一致性边界
聚合根是唯一对外入口,外部不能随意改聚合内部对象。
- **领域服务(Domain Service)**:不适合放在某个实体里的业务规则
- **领域事件(Domain Event)**:领域里发生了“重要事实”,用于解耦和异步扩展
- **仓储(Repository)**:以“聚合”为单位的持久化抽象(不是表级 CRUD)

---

## 聚合:文章里提到的“事务一致性边界”如何理解?

**聚合的本质**:你愿意为哪些对象付出“强一致(同一事务)”的成本。
DDD 建议:**尽量小聚合**,否则并发、性能、锁竞争都会变差。

举个电商例子:

- `Order`(订单)作为聚合根
- `OrderItem`(订单项)属于 `Order` 聚合内部

那么外部修改订单项通常要通过聚合根方法:

```java
class Order {
private OrderId id;
private List items;
private OrderStatus status;

public void changeItemQuantity(SkuId skuId, int qty) {
if (status != OrderStatus.DRAFT) throw new IllegalStateException("不可修改");
// 在聚合内维护不变量:数量>0、库存校验策略等
}
}
```

**不要**让别的模块直接拿到 `OrderItemRepository` 去改某一行记录,因为这会绕过聚合不变量与一致性规则。

---

## DDD 与“模块化单体 / 微服务”的关系

DDD 不是“必须微服务”,反而常见正确路径是:

- 先用 DDD 把单体做成**模块化单体**:
- 每个限界上下文 = 一个清晰模块
- 模块之间只通过接口/事件交互
- 当组织和交付节奏需要时,再把某些上下文拆成**独立服务**
这时你会发现:边界、数据归属、契约早已更清晰,拆分成本更低。

---

## 一个简化的 DDD 分层视图(帮助落地)

DDD 常与“整洁架构/六边形架构”结合,形成清晰依赖方向:

```mermaid
flowchart TD
UI["接口层(HTTP/消息/CLI)"] --> APP["应用层(Application)"]
APP --> DOM["领域层(Domain)"]
APP --> INF["基础设施层(Infrastructure)"]
INF --> DB["数据库/缓存/外部服务"]
```

- **应用层**:编排用例(事务、调用领域对象、发布事件),不写复杂规则
- **领域层**:业务规则与不变量核心
- **基础设施层**:技术细节(ORM、MQ 客户端、第三方 SDK)

---

## 你可以继续深入的子主题(建议钻研顺序)

- 限界上下文识别方法:事件风暴(Event Storming)、业务能力地图
- 聚合设计原则:不变量、并发、最终一致性与 Saga
- 领域事件与 EDA 的结合:Outbox、幂等、事件版本兼容
- 防腐层(ACL)实践:如何隔离外部支付/ERP 的模型污染
- Repository 的边界:以“聚合”为单位 vs 以“查询”为单位(与 CQRS 配合)

如果你告诉我你的业务场景(例如订单/支付/库存/内容),我可以用 DDD 的方式帮你画出可能的限界上下文与聚合边界,并指出哪些地方适合用事件解耦、哪些地方必须强一致。