一、DDD是什么?核心思想
领域驱动设计(DDD)是由 Eric Evans 在 2003 年提出的软件开发方法论。它强调以领域模型为核心,通过与领域专家密切协作,将复杂业务问题拆解为可理解、可维护、可扩展的软件结构。
- 领域:业务范围及知识体系(如电商、金融、物流)。
- 模型:对领域知识的抽象表达,贯穿分析、设计、实现全流程。
- 核心思想:让软件结构与业务语言、业务规则高度一致,提升可维护性和适应变化能力。
二、DDD的战略设计与战术设计
1. 战略设计(高层结构)
a) 限界上下文(Bounded Context)
- 明确业务子域边界,每个上下文内模型一致,外部通过接口/集成通信。
- 每个微服务通常对应一个限界上下文。
b) 领域、子域(Domain/Subdomain)
- 核心域(Core Domain):企业竞争力核心,重点建模。
- 支撑域(Supporting Domain):辅助业务,通常可用通用方案。
- 通用域(Generic Domain):与行业无关的通用服务(如支付、认证)。
c) 上下文映射(Context Mapping)
- 描述各限界上下文间的关系(如防腐层、共享内核、开放主机等)。
2. 战术设计(代码层落地)
a) 实体(Entity)
- 有唯一标识的业务对象(如订单、用户)。
- 生命周期内标识不变,属性可变。
b) 值对象(Value Object)
- 无唯一标识,属性决定其身份(如金额、地址、时间区间)。
- 不可变、可复用、可作为实体属性。
c) 聚合(Aggregate)与聚合根(Aggregate Root)
- 聚合:业务一致性边界,聚合根是唯一入口(如订单聚合根管理订单行)。
- 保证聚合内部数据一致性,跨聚合通过领域服务或事件。
d) 领域服务(Domain Service)
- 业务逻辑不归属于某个实体/值对象时,抽象为领域服务(如定价服务、风控服务)。
e) 仓储(Repository)
- 抽象持久化操作,负责聚合根的存取(如订单仓储)。
- 屏蔽底层数据库细节。
f) 工厂(Factory)
- 负责复杂对象或聚合的创建过程。
三、DDD关键模式与工程实现
1. 领域模型与防腐层
- 领域模型用业务语言(Ubiquitous Language)表达,避免技术语言侵入。
- 防腐层(Anticorruption Layer, ACL):上下文间集成时防止外部模型污染核心模型。
2. 事件驱动与领域事件
- 领域事件(Domain Event):重要业务变化(如订单已支付),用于解耦聚合、跨上下文通信。
- 支持事件溯源(Event Sourcing)、CQRS(命令查询职责分离)。
3. 应用层与基础设施层
- 应用层(Application Service):编排领域对象,处理用户请求、事务。
- 基础设施层(Infrastructure):技术细节(如数据库、消息总线、外部API),通过依赖倒置注入领域层。
4. 分层架构推荐
┌─────────────────────┐
│ 表现层 │ 控制器、API、DTO
├─────────────────────┤
│ 应用层 │ 应用服务、编排、事务
├─────────────────────┤
│ 领域层 │ 实体、值对象、聚合、领域服务
├─────────────────────┤
│ 基础设施层 │ 仓储、外部服务、持久化
└─────────────────────┘
四、DDD与微服务/现代架构的关系
- 微服务边界 = 限界上下文边界:每个微服务聚焦单一业务子域,模型独立,通信松耦合。
- 事件驱动架构:领域事件实现服务间解耦、异步集成。
- CQRS/事件溯源:复杂业务可用命令/查询分离、事件溯源提升可扩展性和审计能力。
- 高内聚低耦合:DDD天然支持微服务架构的高内聚、低耦合需求。
五、常见误区与工程落地建议
1. 常见误区
- 只做分层,不做领域建模,导致“贫血模型”(只有属性没有行为)。
- 领域模型被数据库、DTO、外部系统污染,丧失业务表达力。
- 过度追求“纯DDD”,忽略实际交付效率。
2. 落地建议
- 与领域专家密切协作,业务语言驱动建模。
- 优先建模核心域,支撑/通用域可用通用方案或外包。
- 聚焦聚合边界与一致性,避免跨聚合事务。
- 仓储只暴露聚合根,持久化细节通过依赖倒置解耦。
- 领域事件驱动跨上下文集成,防腐层保护核心模型。
- 结合微服务、自动化测试、持续交付,提升团队协作与交付效率。
六、实践案例(电商订单领域简化示例)
// 实体
public class Order {
private OrderId id;
private List<OrderItem> items;
private OrderStatus status;
public void pay(Payment payment) {
if (status != OrderStatus.CREATED) throw new IllegalStateException("状态错误");
// 业务逻辑
this.status = OrderStatus.PAID;
// 发布领域事件
DomainEventPublisher.publish(new OrderPaidEvent(id, payment));
}
}
// 值对象
public class OrderItem {
private String sku;
private int quantity;
private Money price;
}
// 仓储
public interface OrderRepository {
Order findById(OrderId id);
void save(Order order);
}
// 领域服务
public class PricingService {
public Money calculateTotal(Order order) { ... }
}
七、参考资料与工具
- 《领域驱动设计:软件核心复杂性应对之道》(Eric Evans)
- 《实现领域驱动设计》(Vaughn Vernon)
- DDD Reference
- 微服务设计(Sam Newman)
- EventStorming方法
- Axon、Eventuate、Spring DDD框架
八、总结
领域驱动设计(DDD)是复杂业务系统建模与实现的最佳实践。它通过限界上下文、领域模型、聚合、事件驱动等模式,将软件结构与业务知识深度融合,提升系统的可维护性、可扩展性和响应变化能力。实际工程应结合 DDD 战略与战术设计、微服务架构、自动化测试与持续交付,打造高质量的现代企业级系统。
九、限界上下文划分
1. 概念与原则
- 限界上下文(Bounded Context):业务模型的边界,每个上下文内模型语义一致,外部通过接口/集成通信。
- 划分原则:
- 业务高内聚,低耦合
- 领域专家语言驱动
- 跨上下文通过事件、API或消息集成
2. 电商订单领域示例
- 订单上下文(Order Context):订单生命周期、订单聚合、订单状态变更
- 库存上下文(Inventory Context):库存管理、库存扣减、库存同步
- 支付上下文(Payment Context):支付处理、退款、结算
- 用户上下文(User Context):用户信息、地址、账户
每个上下文可对应一个微服务,独立部署和演进。
十、事件驱动架构
1. 概念
- 领域事件(Domain Event):代表业务状态的重要变化,如“订单已创建”、“订单已付款”。
- 事件发布/订阅:上下文之间通过事件异步解耦、集成。
2. 电商订单领域事件链
- 用户下单,订单服务发布
OrderCreated事件 - 库存服务订阅
OrderCreated,进行库存预扣,发布InventoryReserved事件 - 支付服务订阅
InventoryReserved,处理支付,发布OrderPaid事件 - 订单服务订阅
OrderPaid,变更订单状态为已支付
3. 技术实现
- 消息总线(如 Kafka、RabbitMQ、RocketMQ)负责事件分发
- 事件消息体建议用领域语言和事件溯源ID
十一、聚合建模
1. 概念
- 聚合(Aggregate):业务一致性边界,聚合根是唯一入口。
- 聚合根(Aggregate Root):负责聚合内部所有业务规则和状态变更。
2. 订单聚合建模示例
public class Order { // 聚合根
private OrderId id;
private List<OrderItem> items;
private OrderStatus status;
public void pay(Payment payment) {
if (status != OrderStatus.CREATED) throw new IllegalStateException("订单状态错误");
this.status = OrderStatus.PAID;
DomainEventPublisher.publish(new OrderPaidEvent(id, payment));
}
public void cancel() {
if (status == OrderStatus.PAID) throw new IllegalStateException("已支付订单不能取消");
this.status = OrderStatus.CANCELLED;
DomainEventPublisher.publish(new OrderCancelledEvent(id));
}
}
- 订单聚合根负责所有业务规则(如支付、取消),外部只能通过聚合根操作。
十二、CQRS 与事件溯源
1. CQRS(命令查询职责分离)
- 命令模型(Command Model):处理写操作(如下单、支付、取消),关注业务规则、一致性。
- 查询模型(Query Model):处理读操作(如订单详情、订单列表),关注性能和数据展现。
- 两者可独立扩展,优化性能和复杂度。
2. 事件溯源(Event Sourcing)
- 系统状态由事件流驱动,每个领域事件都持久化(如 OrderCreated, OrderPaid)。
- 聚合根通过重放事件流恢复当前状态,实现审计、回滚和异步集成。
3. 实践示例
a) 命令模型
// 下单命令
public class CreateOrderCommand {
private List<OrderItem> items;
private UserId userId;
}
// 处理器
public class OrderCommandHandler {
public void handle(CreateOrderCommand cmd) {
Order order = OrderFactory.create(cmd.items, cmd.userId);
orderRepository.save(order);
DomainEventPublisher.publish(new OrderCreatedEvent(order.getId()));
}
}
b) 查询模型
public class OrderQueryService {
public OrderDTO getOrderDetail(OrderId id) {
// 查询读库或ES等
}
}
c) 事件溯源
// 事件存储
public interface EventStore {
void append(Event event);
List<Event> loadEvents(AggregateId id);
}
// 聚合重建
public class Order {
public static Order replay(List<Event> events) {
Order order = new Order();
for (Event e : events) {
order.apply(e);
}
return order;
}
}
十三、工程实践建议
- 限界上下文清晰划分,服务间通过事件异步解耦。
- 聚合根只暴露业务操作,保证聚合内一致性,跨聚合用领域服务或事件。
- CQRS提升读写性能,事件溯源实现审计与回滚。
- 事件消息体用领域语言,消息总线可靠投递。
- 结合自动化测试、DevOps、持续交付,实现团队高效协作与系统可维护性。
十四、电商订单领域详细实践案例
1、领域分析与限界上下文划分
1.1. 主要限界上下文(Bounded Context)
- 订单上下文(Order Context):订单生命周期、订单聚合、订单状态变更
- 库存上下文(Inventory Context):库存扣减、库存同步
- 支付上下文(Payment Context):支付处理、退款
- 用户上下文(User Context):用户信息、收货地址
通常每个上下文对应一个微服务。
2、订单聚合建模
2.1. 实体、值对象、聚合根
// 订单聚合根
public class Order {
private OrderId id;
private List<OrderItem> items;
private OrderStatus status;
private UserId userId;
public void pay(Payment payment) {
if (status != OrderStatus.CREATED) throw new IllegalStateException("订单状态错误");
this.status = OrderStatus.PAID;
DomainEventPublisher.publish(new OrderPaidEvent(id, payment));
}
public void cancel() {
if (status == OrderStatus.PAID) throw new IllegalStateException("已支付订单不能取消");
this.status = OrderStatus.CANCELLED;
DomainEventPublisher.publish(new OrderCancelledEvent(id));
}
}
// 值对象
public class OrderItem {
private String sku;
private int quantity;
private Money price;
}
3、领域服务
public class PricingService {
public Money calculateTotal(List<OrderItem> items) {
// 复杂定价策略
}
}
4、仓储实现(Repository)
public interface OrderRepository {
Order findById(OrderId id);
void save(Order order);
}
5、事件驱动与集成
5.1. 领域事件
public class OrderPaidEvent {
private OrderId orderId;
private Payment payment;
// 构造方法、getter...
}
5.2. 事件发布与订阅(伪代码)
- 订单服务发布
OrderPaidEvent - 库存服务订阅
OrderPaidEvent,进行库存扣减 - 支付服务发布
PaymentCompletedEvent,订单服务订阅后变更状态
实际工程中用消息队列(Kafka、RabbitMQ)实现解耦。
6、CQRS 与事件溯源
6.1. 命令与查询分离
- 命令模型:执行下单、支付、取消,写入事件存储
- 查询模型:从事件流或快照重建订单详情,或用专门的读库/ES优化查询
6.2. 事件溯源
public interface EventStore {
void append(Event event);
List<Event> loadEvents(AggregateId id);
}
public class Order {
public static Order replay(List<Event> events) {
Order order = new Order();
for (Event e : events) {
order.apply(e);
}
return order;
}
}
7、跨上下文集成与防腐层
- 订单上下文与库存、支付上下文通过事件异步集成,采用防腐层(Anticorruption Layer)适配外部消息格式,保护领域模型纯洁性。
7、工程实践建议
- 限界上下文清晰,微服务边界与业务一致。
- 聚合根只暴露业务操作,保证聚合内一致性。
- 领域事件驱动跨服务集成,消息可靠投递。
- CQRS/事件溯源提升系统弹性与可审计性。
- 仓储只操作聚合根,持久化细节与领域解耦。
- 持续与领域专家沟通,保持业务语言一致。
9、示意流程
- 用户下单 → 订单服务创建订单 → 发布
OrderCreatedEvent - 库存服务监听,扣减库存 → 发布
InventoryReservedEvent - 支付服务监听,处理支付 → 发布
OrderPaidEvent - 订单服务监听,变更状态为已支付
创作不易,点赞关注,互通有无!
3123

被折叠的 条评论
为什么被折叠?



