在领域驱动设计(DDD)中,战略设计和战术设计是两个不同的层次,分别解决不同粒度的问题。
1. 战略设计(Strategic Design)
定义:
战略设计关注高层次的架构和业务边界,定义系统的整体结构,确保各个领域和团队的协调工作。其核心是划分限界上下文(Bounded Context),并识别核心领域,从而为业务建模提供清晰的边界和指导。
核心概念:
- 限界上下文(Bounded Context): 定义不同业务子领域的边界,确保模型在该边界内的一致性。
- 领域(Domain): 业务的整体范畴,例如电商、物流、支付等。
- 子领域(Subdomain): 具体的业务部分,例如电商中的“订单管理”、“库存管理”。
- 上下文映射(Context Mapping): 描述限界上下文之间的关系(合作、依赖、反腐等模式)。
- 核心域、支撑域、通用域的划分: 确定哪些部分是企业的核心竞争力,哪些是通用或辅助功能。
举例:
假设你正在设计一个在线购物平台,可以将其分为多个限界上下文:
- 订单管理上下文: 处理订单的创建、支付和发货。
- 用户管理上下文: 处理用户注册、登录和账户管理。
- 库存管理上下文: 负责库存的增加、减少以及供应链管理。
- 支付上下文: 处理支付网关对接、账单管理等。
在战略设计阶段,你需要明确:
- 这些上下文之间的边界,避免相互干扰。
- 如何沟通,例如订单系统和库存系统通过API交互。
- 识别核心域(订单),支撑域(用户管理),以及通用域(支付)。
2. 战术设计(Tactical Design)
定义:
战术设计关注具体领域模型的实现,即如何在每个限界上下文内部设计领域对象,保证业务逻辑的合理性和一致性。其核心在于用面向对象的方式来设计领域模型。
核心概念:
- 实体(Entity): 有唯一标识的业务对象,例如“订单编号”。
- 值对象(Value Object): 无唯一标识,通常不可变,例如“地址”。
- 聚合(Aggregate): 一组具有业务完整性的对象集合,例如“订单”聚合包含订单明细、收件人信息。
- 聚合根(Aggregate Root): 负责管理聚合内的所有对象,保证一致性。
- 领域服务(Domain Service): 处理跨多个实体的复杂业务逻辑。
- 仓储(Repository): 负责持久化聚合对象。
举例:
在“订单管理上下文”中,战术设计的实现可能包含:
class Order {
private $orderId;
private $items = [];
private $status;
public function __construct($orderId) {
$this->orderId = $orderId;
$this->status = "pending";
}
public function addItem(Product $product, $quantity) {
$this->items[] = new OrderItem($product, $quantity);
}
public function confirmOrder() {
if (empty($this->items)) {
throw new Exception("Order must have at least one item.");
}
$this->status = "confirmed";
}
public function getStatus() {
return $this->status;
}
}
class OrderItem {
private $product;
private $quantity;
public function __construct(Product $product, $quantity) {
$this->product = $product;
$this->quantity = $quantity;
}
}
Order
是一个实体,具有唯一的orderId
。OrderItem
是值对象,表示订单中的具体商品项。- 订单管理逻辑封装在
confirmOrder()
方法中,确保订单完整性。
3. 战略设计 vs. 战术设计的区别
对比维度 | 战略设计(Strategic Design) | 战术设计(Tactical Design) |
---|---|---|
关注点 | 业务边界、上下文划分、架构层次 | 领域模型、对象设计、业务逻辑 |
主要目标 | 划分清晰的上下文,减少依赖 | 设计良好的领域模型以执行业务逻辑 |
设计内容 | 限界上下文、上下文映射、领域划分 | 实体、值对象、聚合、服务、仓储等 |
粒度 | 高层次(整个系统级) | 低层次(某个上下文内部) |
结果 | 系统架构、服务划分 | 具体代码实现 |
4. 总结
- 战略设计:站在业务全局角度,划分系统的各个上下文和边界,明确它们如何协作。例如,将“订单管理”与“库存管理”区分开来。
- 战术设计:深入到具体实现层面,设计领域对象及其交互方式。例如,定义订单如何管理商品项、如何更改状态。
简单类比:
- 战略设计 = 规划城市布局,决定住宅区、商业区、工业区。
- 战术设计 = 具体在住宅区中,设计楼房的结构、房间布局等。
是的,战略设计和战术设计是**领域驱动设计(DDD)**中不可缺少的两个关键部分。它们共同作用,确保在复杂业务领域中,系统既具备高层次的架构规划,又能落实到细节的领域模型实现。两者相辅相成,缺一不可。
为什么战略设计不可缺少?
1. 确保业务与技术对齐
- 战略设计帮助团队从业务角度划分系统,定义限界上下文(Bounded Context),确保每个部分都有清晰的业务职责,避免“一个模型打天下”的混乱局面。
- 如果缺少战略设计,系统很可能变得耦合混乱,导致业务逻辑交错难以维护。
2. 解决复杂业务的划分问题
- 在复杂业务场景中,如电商、金融等,往往有多个子系统,例如“订单管理”“库存管理”“支付处理”等。战略设计可以有效划分边界,减少模块之间的依赖,实现团队协作的独立性。
- 如果没有战略设计,可能会导致职责不清、业务逻辑相互干扰,维护困难。
3. 支持团队协作
- 大型企业通常由不同团队负责不同的业务子领域。战略设计确保各团队在清晰边界下开发,降低沟通成本,提升开发效率。
- 没有战略设计,不同团队可能会在同一领域重复建模,造成数据不一致和开发浪费。
4. 促进技术架构的演进
- 战略设计能够确保系统在扩展性和可维护性上的合理规划,比如微服务架构的拆分,或系统的模块化演进。
- 如果没有战略设计,系统可能会在后期由于不合理的架构决策而面临高昂的改造成本。
总结:
战略设计不可缺少,因为它从宏观层面帮助团队建立合理的架构边界,确保业务逻辑的清晰性、团队协作的顺畅性,以及系统的长期可扩展性。
为什么战术设计不可缺少?
1. 确保领域逻辑的正确性
- 战术设计关注具体的领域模型,比如如何设计实体(Entity)、值对象(Value Object)、聚合(Aggregate)等,以保证业务逻辑的一致性和完整性。
- 如果缺少战术设计,领域逻辑可能会散落在代码的各个角落,导致难以维护和扩展。
2. 避免贫血模型,聚合业务逻辑
- 通过战术设计,业务逻辑被放入领域对象中,而不是仅仅依赖于服务或数据库。这样可以减少**“贫血模型”**(只存储数据,不包含行为)的情况,使模型更贴近业务。
- 没有战术设计,系统容易演变成一个CRUD式架构,业务规则分散在控制器、服务层,导致高耦合、低内聚。
3. 提供灵活的业务扩展能力
- 通过战术设计,利用**聚合根(Aggregate Root)和领域服务(Domain Service)**来封装复杂的业务逻辑,确保系统能随业务需求的变化进行调整。
- 缺少战术设计,扩展业务时往往会修改多个模块,增加开发和维护成本。
4. 确保一致性和事务控制
- 战术设计通过聚合的概念,控制数据的一致性,确保对聚合的操作在事务边界内完成,避免数据不一致问题。
- 如果没有战术设计,事务逻辑可能会在多个组件之间分散,导致数据处理混乱。
总结:
战术设计不可缺少,因为它从微观层面确保系统的业务逻辑实现合理、可维护,避免贫血模型,提高系统的可扩展性。
两者如何相辅相成?
如果只做战略设计,而不做战术设计:
- 可能会有清晰的架构规划,但在实现细节上缺乏合理的业务建模,导致代码实现复杂、难以维护。
如果只做战术设计,而不做战略设计:
- 可能会有合理的领域建模,但在整体架构上缺乏边界划分,导致不同模块之间相互耦合,难以扩展。
一个完整的DDD实施流程应该是这样的:
- 先做战略设计,确定各业务上下文的边界、团队分工。
- 然后做战术设计,在每个上下文内部实现高质量的领域模型。
举例:电商系统
层次 | 战略设计 | 战术设计 |
---|---|---|
定义 | 识别业务边界,划分上下文 | 设计领域对象,确保业务逻辑一致性 |
示例 | 划分“订单管理”“库存管理”上下文 | 定义“订单”实体、聚合、服务 |
结果 | 清晰的架构边界,避免模块耦合 | 代码层面业务逻辑清晰可维护 |
结论
战略设计和战术设计都是DDD不可或缺的组成部分。
- 战略设计决定系统的边界和结构,帮助管理复杂性;
- 战术设计确保业务逻辑的实现,保证代码的高质量。
DDD成功实施的关键在于:
"战略设计决定大的方向,战术设计决定落地效果,二者缺一不可。"