# 战术设计

> DDD战术设计总览：在限界上下文内部用实体、值对象、聚合、工厂、仓储、领域服务、领域事件等构件对业务进行精细化建模。

- Skill: `microwind/skill-81` (Agent Skill)
- Install (CLI): `npx skillmds@latest add microwind/skill-81`
- Raw SKILL.md: https://api.skillmd.com/api/skills/microwind/skill-81/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: microwind (https://skillmd.com/u/microwind)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/microwind/skill-81

---


# 战术设计 (Tactical Design)

## 概述

战术设计是在 [战略设计](../strategic-design/) 确定的**限界上下文内部**，对具体业务场景进行**精细化建模**的工程手段。它关注"**怎么写代码**"才能让业务规则稳定、可维护、可演进。

**核心特征**：

- **限定在单一限界上下文内** ——不跨上下文讨论
- **面向模型和代码** ——产出物是类、聚合、接口
- **工程师的主要战场** ——开发日常工作的核心

**核心问题**：

```
1. 哪些对象有身份，哪些只是值？         → 实体 vs 值对象
2. 哪些对象必须一起变更？               → 聚合
3. 谁负责对象的创建？                   → 工厂
4. 对象如何持久化？                     → 仓储
5. 不属于任何对象的业务逻辑放哪里？       → 领域服务
6. 如何解耦聚合间的协作？               → 领域事件
```

## 战术设计 vs [战略设计](../strategic-design/)

```
战略设计        ──▶    战术设计         ──▶    代码实现
(宏观)                 (微观)                (工程)
 │                      │                      │
 ├─ 领域                ├─ 实体                ├─ Class / Struct
 ├─ 子域                ├─ 值对象              ├─ Immutable Object
 ├─ 限界上下文          ├─ 聚合根              ├─ Aggregate Root
 └─ 上下文映射          ├─ 工厂                ├─ Factory
                       ├─ 仓储                ├─ Repository Impl
                       ├─ 领域服务            ├─ Service Class
                       └─ 领域事件            └─ Event + Handler
```

## 战术设计的七大构件

Eric Evans 在蓝皮书中给出的原始战术模式包括 **实体、值对象、聚合、工厂、仓储、服务**（含领域服务）。**领域事件**虽未在原书的"模型驱动设计构造块"章节正式列出，但 Evans 在后续会议演讲与 Vaughn Vernon 的 IDDD 中已将其纳入战术设计的核心构件。本文采用 7 件套的常见划分：

> ⚠️ **按需选用，不为标准而引入**：七大构件是**工具箱**，不是"DDD 项目必须全部齐备"的检查清单。简单的 CRUD 类子域、原型期项目，使用其中 2–3 件（如实体 + 仓储）足矣；只有核心子域、业务规则密集的场景才需要工厂、领域服务、领域事件等"重武器"全部上场。**复杂度匹配是 DDD 落地的根本判据**。

### 1. [实体 (Entity)](../entity/)

```
定义：拥有唯一标识和生命周期的业务对象。
典型：User、Order、Subscription
判别：通过 ID 判断相等，而非属性值相等。

代码形态（四种血液模型）：
  ▸ 失血模型：只有字段，业务逻辑外泄      ❌ 不推荐
  ▸ 贫血模型：数据 + 核心业务规则         ✅ 工程首选
  ▸ 充血模型：数据 + 所有业务（含持久化）  ⚠️ 少用
  ▸ 胀血模型：还掺入授权、事务等横切关注点  ❌ 禁用
```

### 2. [值对象 (Value Object)](../value-object/)

```
定义：没有身份、通过属性值判断相等、不可变的对象。
典型：Money、Email、Address、DateRange
判别：不需要"哪个"，只关心"是什么"。

分类：
  ▸ 单一属性值对象：Email、Phone（只有一个值）
  ▸ 多属性值对象：Money（amount + currency）、Address（province + city + street）

价值：封装领域概念，消除"原始类型偏执"。
```

### 3. [聚合与聚合根 (Aggregate & Aggregate Root)](../aggregate/)

```
定义：一组必须一起变更的实体和值对象的集合。
入口：聚合根是外部访问聚合的唯一入口。
事务：一个事务只修改一个聚合。

设计原则：
  ▸ 尽量小的聚合（高内聚）
  ▸ 聚合间通过 ID 引用，不通过对象引用
  ▸ 聚合内强一致，聚合间最终一致

范围：聚合 < 限界上下文 < 子域 < 领域
```

### 4. [工厂 (Factory)](../factory/)

```
定义：封装复杂对象 / 聚合的创建过程，保证创建时不变量满足。
形态：
  ▸ 独立工厂类：复杂创建，依赖外部服务
  ▸ 聚合根静态工厂方法：常规场景首选
  ▸ 值对象工厂方法：Money.yuan(100)

原则：构造即合法，工厂要防止非法对象诞生。
```

### 5. [仓储 (Repository)](../repository/)

```
定义：聚合的持久化抽象，为领域层提供类似集合的接口。
分层：
  ▸ 接口定义在领域层（依赖倒置）
  ▸ 实现在仓储层 / 基础设施层
  ▸ Mapper / ORM 是仓储的具体实现手段

使用约束：
  ▸ 一个聚合根一个仓储
  ▸ 返回领域对象，不返回数据库实体
  ▸ 保存的是完整聚合
```

### 6. [领域服务 (Domain Service)](../domain-service/)

```
定义：封装不属于任何单个实体或值对象的领域逻辑。
特征：
  ▸ 无状态
  ▸ 纯业务，不管事务、不调基础设施
  ▸ 位于领域层

判断标准：
  如果逻辑不自然属于任何一个实体 → 用领域服务
  优先把逻辑放在实体中，领域服务是补充
```

### 7. [领域事件 (Domain Events)](../domain-events/)

```
定义：领域中已发生且值得关注的事实。
命名：过去时态（OrderPlaced、PaymentCompleted）。
用途：
  ▸ 聚合间解耦通信
  ▸ 跨限界上下文协作
  ▸ 实现最终一致性（配合 Saga / Outbox）
  ▸ 审计 / 事件溯源的原材料
```

## 七大构件的协作关系

```
┌───────────────────────────────────────────────────────────────┐
│                   一个限界上下文                                │
│                                                               │
│                  ┌──────────────┐                             │
│         创建 ──▶ │   工厂         │                             │
│                  └──────┬───────┘                             │
│                         ▼                                     │
│                  ┌──────────────┐        包含                   │
│                  │  聚合根(Entity) │──────────▶ [实体] [值对象]   │
│                  └──────┬───────┘                             │
│                         │ 发布                                 │
│                         ▼                                     │
│                  ┌──────────────┐                             │
│                  │   领域事件     │──────────▶ 其他聚合 / 上下文   │
│                  └──────────────┘                             │
│                         ▲                                     │
│                         │ 持久化                               │
│                  ┌──────┴───────┐                             │
│                  │    仓储        │                             │
│                  └──────────────┘                             │
│                         ▲                                     │
│                         │ 调用                                 │
│                  ┌──────┴───────┐                             │
│         跨聚合 ──▶│   领域服务     │                             │
│                  └──────────────┘                             │
└───────────────────────────────────────────────────────────────┘
```

## 战术设计落地：典型用例流程

以 RabbitAdvisors **用户订阅专栏** 为例：

```
1. 应用服务接收命令 SubscribeCommand
        │
        ▼
2. 领域服务 SubscriptionDomainService.subscribe()
        │
        ├─▶ 查专栏（通过防腐层 ColumnFacadeClient）
        │
        ├─▶ 工厂 OrderFactory.create(reader, column) 创建订单聚合
        │
        ├─▶ 支付（通过 PaymentClient）→ Payment 聚合
        │
        ├─▶ Subscription.fromPayment(payment, column) 工厂方法创建订阅聚合
        │
        └─▶ 订阅根发布 SubscriptionCreated 领域事件
        │
        ▼
3. 仓储 SubscriptionRepository.save() 持久化
        │
        ▼
4. 应用服务发布事件 → 其他上下文订阅
        │
        ├─▶ 成长体系上下文：增加积分
        └─▶ 签约分佣上下文：累计到作者佣金
```

## 战术设计常见误区

```
❌ 贫血模型 + 万能 Service
   → 实体只剩 getter/setter，业务逻辑全在 Service
   → 丢失了 DDD 最有价值的充血模型思想

❌ 聚合过大
   → 一个聚合包含 User + Order + Product + Payment
   → 事务冲突严重，聚合间耦合度高

❌ 聚合之间直接对象引用
   → order.customer.updateAddress() 这样调用
   → 应该用 customerId 查找然后独立操作

❌ 工厂只是构造函数的"套壳"
   → 没有增加任何业务不变量校验
   → 应该避免，直接用构造函数即可

❌ 领域服务替代实体行为
   → OrderService.cancel(order) 替代 order.cancel()
   → 除非确实跨聚合，否则应该让行为回归实体

❌ 领域事件用命令式命名
   → "ProcessPayment" 是命令，"PaymentProcessed" 才是事件

❌ 仓储返回数据库实体
   → 把 DO 泄漏到领域层
   → 仓储必须返回领域对象
```

## 战术设计与[分层架构](../layered-architecture/)的映射

```
战术构件                位置                包命名示例
─────────────────────────────────────────────────────────
实体 / 聚合根            domain 层           subscription.Subscription
值对象                   domain 层           subscription.SubscriptionId
工厂                     domain 层           subscription.SubscriptionFactory
领域服务                 domain 层           subscription.SubscriptionDomainService
领域事件                 domain 层           subscription.events.SubscriptionCreated
仓储接口                 domain 层           subscription.SubscriptionRepository
仓储实现                 repository 层        subscription.SubscriptionRepositoryImpl
```

## 与其他 DDD 概念的关系

| 概念 | 关系 |
|------|------|
| [战略设计](../strategic-design/) | 战略先行，战术在限界上下文内展开 |
| [领域模型](../domain-model/) | 七大构件是领域模型的砖块 |
| [领域建模](../domain-modeling/) | 建模过程的产物就是战术设计构件 |
| [分层架构](../layered-architecture/) | 战术构件分布到各层中实现 |
| [通用语言](../ubiquitous-language/) | 战术构件的命名必须用通用语言 |

## 总结

**核心**：战术设计在限界上下文内用七大构件把业务建模成代码。

**七大构件**：实体、值对象、聚合、工厂、仓储、领域服务、领域事件。

**精髓**：

- 优先用**充血模型**让业务逻辑回归领域对象
- 聚合要**小**，通过 **ID 引用**解耦
- 领域层不依赖技术细节，通过**仓储接口**倒置依赖
- 用**领域事件**实现聚合间最终一致性

**记住**：战术设计不是目的，让业务表达更清晰、让代码更可维护才是。

