3. 战略设计 vs 战术设计:战略设计的宏观视角与战术设计的微观实现
领域驱动设计(DDD)并非一套单一的技术实践,而是由两个不同抽象层次的设计活动构成:战略设计(Strategic Design)与战术设计(Tactical Design)。理解二者的区别与协作关系,是掌握 DDD 的关键分水岭。
战略设计关注系统的“宏观结构”——划定业务边界、识别核心领域、建立通用语言;战术设计则关注“微观实现”——在限界上下文内部,用实体、值对象、领域服务、聚合等模式精确建模业务逻辑。二者如同城市规划与单体建筑设计:战略决定“在哪里建、建什么”,战术决定“怎么建、用什么材料”。
3.1 战略设计:划定战场与统一语言
战略设计的目标是让团队在复杂的业务领域中达成共识,避免“鸡同鸭讲”式的沟通混乱。它主要解决三个问题:
- 限界上下文(Bounded Context):将系统拆分为语义自治的边界,每个上下文内保持概念一致,上下文之间通过上下文映射(Context Map)协作。
- 核心域(Core Domain):识别业务中最具竞争力的部分,集中资源投入;支撑域(Supporting Subdomain)和通用域(Generic Subdomain)则考虑复用或外购。
- 通用语言(Ubiquitous Language):在团队(业务专家+开发人员)内部建立统一的术语体系,确保代码、文档、讨论使用同一套词汇。
战略设计核心产出: 限界上下文地图 + 上下文映射关系 + 核心域识别报告 + 通用语言词汇表。
3.2 战术设计:在边界内精雕细琢
一旦限界上下文划定完毕,战术设计便进入每个上下文内部,使用 DDD 的“建筑块”模式实现业务逻辑。战术设计的主要模式包括:
| 战术模式 | 职责 | 典型特征 |
|---|---|---|
| 实体(Entity) | 具有唯一标识、生命周期可追踪的对象 | ID 属性、可变状态、equals() 基于 ID |
| 值对象(Value Object) | 描述属性,无标识,不可变 | 所有属性在构造时赋值、无 setter、可替换 |
| 聚合(Aggregate) | 实体与值对象的边界封装,保证数据一致性 | 一个根实体(Aggregate Root)、内部引用、外部只能通过根访问 |
| 领域服务(Domain Service) | 处理不适合放在实体或值对象中的业务逻辑 | 无状态、操作多个聚合、命名体现业务动作 |
| 领域事件(Domain Event) | 记录领域中发生的业务事件,用于解耦 | 过去时命名、包含事件发生时的上下文数据 |
| 仓储(Repository) | 提供聚合的持久化与检索接口 | 面向集合的接口、隐藏存储细节 |
实践建议: 战术设计应始终以“业务语言”驱动。例如,在订单上下文中,
Order 是一个实体(有订单号),Money 是一个值对象(金额+币种),PlaceOrderService 是一个领域服务(协调订单与库存)。代码中的类名、方法名、参数名都应直接反映通用语言。
3.3 战略 vs 战术:关键差异对比
下表从多个维度对比战略设计与战术设计的核心差异:
| 对比维度 | 战略设计 | 战术设计 |
|---|---|---|
| 关注范围 | 系统级、组织级 | 限界上下文内部 |
| 主要产出 | 限界上下文地图、上下文映射、核心域识别 | 实体、值对象、聚合、领域服务、仓储 |
| 决策者 | 架构师 + 业务专家 + 技术负责人 | 开发团队 + 领域专家 |
| 变更频率 | 低(数月甚至数年调整一次) | 高(每次迭代都可能调整) |
| 错误代价 | 极高(边界错误导致大量返工) | 中等(重构成本相对可控) |
| 技术绑定 | 与具体技术栈无关 | 可结合 ORM、事件总线、CQRS 等实现 |
3.4 从战略到战术:协作流程
战略设计与战术设计并非先后顺序的瀑布式活动,而是迭代反馈的协作过程。典型的工作流如下:
- 事件风暴(Event Storming):业务专家与开发团队一起,用领域事件串联业务流程,初步识别限界上下文。
- 战略建模:绘制上下文映射图,确定核心域、支撑域、通用域,定义上下文之间的协作关系(如防腐层、开放主机服务、发布语言)。
- 战术建模:在每个限界上下文内部,通过聚合设计、实体/值对象建模、领域服务编排,将业务规则转化为代码。
- 验证与反馈:通过测试(单元测试、集成测试)验证战术模型是否准确反映业务需求;若发现边界模糊或逻辑冲突,返回步骤 1 或 2 调整战略模型。
3.5 常见误区与避坑指南
误区一:跳过战略设计直接进入战术设计。 很多团队拿到需求后直接开始写实体和聚合,结果导致上下文边界混乱、通用语言不一致,后期合并或拆分成本极高。正确的做法是:先花 1-2 天做事件风暴和上下文映射,再进入编码。
误区二:战略设计只做一次,后续不再调整。 业务是动态演化的,限界上下文和核心域识别也需要随业务变化而调整。建议每个大迭代(如每季度)回顾一次上下文映射图,确保其与当前业务对齐。
误区三:战术设计过度复杂化。 并非所有业务逻辑都需要用 DDD 的战术模式。对于简单的 CRUD 操作,使用传统分层架构即可;DDD 的战术模式应保留给真正复杂的业务规则和一致性要求高的场景。