# DDD 落地原则（DDD Principles）

> 状态：**已决策**
> 背景：ts-libs 会话「dd DDD 扩展讨论」；姊妹篇：[aggregate.md](./aggregate.md)（聚合/仓储规划）、[domain-event.md](./domain-event.md)（领域事件规划）

## 1. 决策摘要

1. **不纠结实体胖瘦**——胖瘦是结果，复用是判据；
2. **领域对象全部是 plain POJO**——纯数据、无行为（贫血模型，但规则不散落）；
3. **不纠结代码写在哪里**——`order.cancel()` 与 `service.cancel(order)` 没有实质区别；
4. **规则归属只有两条路**：
   - **不需要调用外部**（纯函数、单值校验）→ **utils 谓词**（已落地）；
   - **需要做流程**（编排、跨步骤、跨聚合）→ **service 方法**（已支持）。

**推论**：DDD 落地**不需要新增"实体方法"概念**——现有 `service_schema + flow` 已是规则复用归属地，`AggregateSchema` / 领域事件仍按各自规划做。

## 2. 为什么这样定

### 2.1 胖瘦是假问题，复用才是本质

```
一个规则 → 被几处用？
  被 1 处用  → 留在 flow 里，不为理论正确而挪
  被 ≥2 处用 → 提取到归属地（utils / service），一处维护
```

"取消"进归属地，不是因为实体该胖，而是因为用户取消、超时自动取消、客服取消三个流程复用同一套取消规则。没有复用，留在 flow 完全合理。

### 2.2 归属地之争是假问题

```
order.cancel()              service.cancel(order)
─── ──────────              ─── ──────────────────
规则住在 Order 对象          规则住在 Service 对象
flow: invoke(order.cancel)  flow: invoke(service.cancel, order)
```

两者都是"规则提取 + 复用"，无实质区别。**分水岭不在 order.cancel vs service.cancel，在"有没有 cancel 这个提取物"**——有就是规则内聚，没有就是散落。

### 2.3 复用 ≠ 抽象

- 抽象/预测（坏）："未来可能用到，先抽出来" → 过度设计；
- 复用/实际（好）："现在被三个流程用到，提取" → 唯一正确触发条件。

遵循"三行相似胜过过早抽象"——用到了才内聚，不预测。

### 2.4 主观标准不可执行（最硬的理由）

**胖瘦标准无法统一**：什么样的实体算"胖"、算"瘦"，每个人判断都不同——有的人觉得状态机进实体是对的，有的人觉得一个方法就算胖。没有客观刻度，团队协作时必然争论，代码评审无法给出确定性结论。

```
实体胖瘦 = 主观判断 → 无法统一标准 → 不可执行、不可 lint
复用归属 = 客观判据 → 标准统一可执行 → 机器可校验

POJO + utils/service 归属的每个判断都是确定性的：
  调不调用外部？→ 是纯函数还是流程？→ 归 utils 还是 service
```

这符合 pylon 的 DSL 哲学：**声明式、机器校验**（lint 是权威事实来源）。"这条规则该放哪"必须能由一个确定的判据回答，而不是靠某个人对"胖瘦"的品味。**规则归属是"放 utils 还是 service"的二分，机器可判；实体胖瘦是"放多少进实体"的连续光谱，人各执一词。** 选二分、弃光谱。

## 3. 规则归属决策表

| 场景 | 归属 | 现状 |
|------|------|------|
| 纯函数/单值校验（如金额两位小数） | utils 谓词 | ✅ 已落地（BLE：AmtUtils 等） |
| 跨步骤业务流程（下单、取消） | service 方法 + flow | ✅ 已支持 |
| 跨聚合协作规则（定价、扣库存） | service 方法（领域服务角色） | ✅ 已支持 |
| 状态机规则（取消校验 + 流转） | service 方法 | ✅ 已支持 |
| 实体方法（order.cancel） | **不采用**（与 service 等价，选零成本） | 不需要 |

## 4. 对扩展规划的影响

- **聚合/仓储（aggregate.md）**：照做——解决多表一致性，不引入实体行为；
- **领域事件（domain-event.md）**：照做——`publish` 由 service（flow）发起，与"规则归属 service"一致；
- **实体方法/defineEntity**：**不立项**——`service.cancel(order)` 已覆盖，避免为一个无实质区别的语法糖增加 DSL 面。