DDD 领域模型

领域驱动设计(Domain-driven design)是一种通过将实现连接到持续进化的模型来满足复杂需求的软件开发方法。

DDD 核心思想是通过领域驱动设计方法定义领域模型,从而确定业务和应用边界,保证业务模型与代码模型的一致性

领域驱动设计是一种由域模型来驱动着系统设计的思想。

  • 一种解决复杂系统的建模与分析方法
  • 将业务概念和规则转为对象和对象的属性行为
  • 通过封装、继承和多态降低业务复杂性
  • 不是数据库表结构的映射,而是对业务逻辑、规则、流程的建模。
  • 包含实体、值对象、聚合、领域服务、领域事件等元素。

举个例子:在一个电商系统中,“订单”不是一个简单的 Order 表,而是一个包含下单、支付、取消、发货等行为和状态流转逻辑的复杂对象。

五层架构

原则:每层只能与位于其下方的层发生耦合。

分层架构可以简单分为两种,即严格分层架构和松散分层架构。在严格分层架构中,某层只能与位于其直接下方的层发生耦合,而在松散分层架构中,则允许某层与它的任意下方层发生耦合。

img
  • 降低了系统的性能。因为增加了中间层,可以通过缓存机制来改善。

  • 可能会导致级联的修改。这种修改尤其体现在自上而下的方向,可以通过依赖倒置来改善。

  • 领域层domain(核心业务层)

    • 领域对象:实体对象、值对象、聚合、聚合根 (充血模型:业务方法在实体对象里,负责维护实体自身的生命周期和状态)
    • 领域服务:接口服务,业务的具体实现(围绕实体):商品服务、电子围栏、订单服务、运单服务、
    • 领域仓储:持久化的细节隐藏,减少领域层对基础设施层的依赖(需要使用到基础设施层,包括DB、Feign调用等)
  • 应用层(业务逻辑层)

    • 应用服务:调用和组合领域层的服务,不包含任何业务逻辑(简单除外),只是组合模型业务(类似模块化)
    • 数据转换:接口参数DTO的简单校验,以及DTO和实体值对象的数据转换
    • 事件订阅:管理多个消息主体
    • 消息订阅:多个微服务间协作解耦的异步实现方式
  • 基础设施层

    • 数据库、缓存、消息队列、配置
    • 对外隐藏技术实现细节,提供粗粒度的数据输出服务
    • 数据库操作:领域层传递的是数据对象,在这里可以按数据表的实现方式进行拆分实现
    • 防腐层(外部API):隔离业务,防止污染业务代码,可通过代理或适配器实现转换
  • 用户接口层(表现层)

    • 面向服务间API调用,WEB接口
    • 身份认证和权限验证
    • 限流和熔断服务
    • VO和DTO数据转换

简单查询不涉及业务,是可以直接从应用层穿透到PO查询,不需要经过domain层

DTO是不能存在于domain层的,DDD设计不认为DTO是业务对象,entity才是

订单建模示例

完整Github项目

  1. DDD 分层模型

用户接口层(Interface / API 层):提供 REST 接口,接收外部请求。

应用层(Application 层):编排领域服务,处理用例逻辑,如“创建订单”。

领域层(Domain 层):包含核心业务逻辑,如 Order 聚合根、领域事件、状态机、仓储接口等。

基础设施层(Infrastructure 层):实现仓储接口、防腐层、外部服务调用、事件发布等。

  1. 防腐层(Anti-Corruption Layer, ACL)

用于与外部系统(如库存服务)交互,避免领域模型被外部污染。

在基础设施层实现,通过 ACL 将外部模型转换为内部领域模型。

  1. 事件与状态机

使用领域事件(如 OrderCreatedEvent, OrderPaidEvent)表达领域内发生的重要事情。

使用状态机(如 OrderStateMachine)管理订单状态的合法流转(如:待支付 -> 已支付 -> 已发货 / 已取消)。

代码结构展示

├── src/
│ └── main/
│ ├── java/
│ │ └── com/example/order/
│ │ ├── OrderApplication.java
│ │ │
│ │ ├── application/ # 应用层(Service)
│ │ │
│ │ ├── domain/ # 领域层(核心)
│ │ │ ├── model/ # 聚合根、实体、值对象
│ │ │ ├── event/ # 领域事件
│ │ │ ├── repository/ # 领域仓库接口
│ │ │ ├── service/ # 领域服务
│ │ │ └── statemachine/ # 状态机配置
│ │ │
│ │ ├── infrastructure/ # 基础设施层
│ │ │ ├── persistence/ # 持久化实现
│ │ │ ├── messaging/ # 消息发布(事件)
│ │ │ └── integration/ # 外部系统集成(含防腐层)
│ │ │
│ │ └── interfaces/ # 接口层(Controller)

订单管理建模

核心对象:

  • 聚合根对象:Order(订单)

  • 值对象:订单项(OrderItem)、地址(Address)、金额(Money)等

  • 一致性边界:一次下单操作必须保证所有订单项和状态一致

实体与值对象定义

  • 值对象(金额)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Money {
private BigDecimal amount;
private String currency;

public Money(BigDecimal amount, String currency) {
if (amount.compareTo(BigDecimal.ZERO) < 0) {
throw new BusinessRuleViolation("金额不能为负");
}
this.amount = amount;
this.currency = currency;
}

public Money add(Money other) {
if (!this.currency.equals(other.currency)) {
throw new BusinessRuleViolation("货币不一致");
}
return new Money(this.amount.add(other.amount), this.currency);
}

// equals, hashCode 基于 amount 和 currency
}
  • 实体(订单项)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class OrderItem {
private String productId;
private String productName;
private Money unitPrice;
private int quantity;
private Money totalPrice;

public OrderItem(String productId, String productName, Money unitPrice, int quantity) {
this.productId = productId;
this.productName = productName;
this.unitPrice = unitPrice;
this.quantity = quantity;
this.totalPrice = unitPrice.multiply(quantity);
}

// 可提供 increaseQuantity(), changePrice() 等方法
}
  • 聚合根(订单)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
public class Order {
private String orderId;
private String customerId;
private List<OrderItem> items = new ArrayList<>();
private Address shippingAddress;
private Money totalAmount;
private OrderStatus status;
private DateTime createdAt;
private List<DomainEvent> events = new ArrayList<>();

// 私有构造,强制使用工厂创建
private Order() {}

// 下单:创建订单
public static Order createFromCart(
String orderId,
String customerId,
List<CartItem> cartItems,
Address address) {

Order order = new Order();
order.orderId = orderId;
order.customerId = customerId;
order.shippingAddress = address;
order.createdAt = LocalDateTime.now();
order.status = OrderStatus.PENDING;

for (CartItem item : cartItems) {
ProductPrice price = ProductService.getCurrentPrice(item.getProductId());
order.addItem(new OrderItem(
item.getProductId(),
item.getProductName(),
price.getAmount(),
item.getQuantity()
));
}

order.calculateTotal();
return order;
}

// 添加订单项(业务规则:仅在 PENDING 状态下允许)
private void addItem(OrderItem item) {
if (status != OrderStatus.PENDING) {
throw new BusinessRuleViolation("订单已锁定,无法添加商品");
}
items.add(item);
}

// 计算总金额
private void calculateTotal() {
this.totalAmount = items.stream()
.map(OrderItem::getTotalPrice)
.reduce(Money.ZERO, Money::add);
}

// 支付成功:确认订单
public void confirmPayment() {
if (status != OrderStatus.PENDING) {
throw new BusinessRuleViolation("订单状态异常,无法确认支付");
}
this.status = OrderStatus.PAID;
// 发布领域事件
DomainEventPublisher.publish(new OrderPaidEvent(orderId, totalAmount));
}

// 取消订单
public void cancel() {
if (status == OrderStatus.SHIPPED) {
throw new BusinessRuleViolation("已发货订单不能取消");
}
if (status == OrderStatus.CANCELLED) {
return; // 幂等处理
}
this.status = OrderStatus.CANCELLED;
DomainEventPublisher.publish(new OrderCancelledEvent(orderId, customerId));
}

// 发货
public void ship() {
if (status != OrderStatus.PAID) {
throw new BusinessRuleViolation("订单未支付,不能发货");
}
this.status = OrderStatus.SHIPPED;
DomainEventPublisher.publish(new OrderShippedEvent(orderId));
}

private void addEvent(DomainEvent event) {
this.events.add(event);
}
// getter 方法省略...
}
  • 枚举及其他

1
2
3
4
5
6
public enum OrderStatus {
PENDING, // 待支付
PAID, // 已支付
SHIPPED, // 已发货
CANCELLED // 已取消
}

领域事件(Events)

领域事件可通过事件总线异步通知库存、物流、用户积分等系统。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 订单已支付
public class OrderPaidEvent implements DomainEvent {
private String orderId;
private Money amount;
private LocalDateTime occurredAt;

public OrderPaidEvent(String orderId, Money amount) {
this.orderId = orderId;
this.amount = amount;
this.occurredAt = LocalDateTime.now();
}
}

// 订单已取消
public class OrderCancelledEvent implements DomainEvent {
private String orderId;
private String customerId;

public OrderCancelledEvent(String orderId, String customerId) {
this.orderId = orderId;
this.customerId = customerId;
}
}

// 订单已发货
public class OrderShippedEvent implements DomainEvent {
private String orderId;

public OrderShippedEvent(String orderId) {
this.orderId = orderId;
}
}

领域服务(Service)

处理跨聚合或跨上下文的复杂逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Service
public class OrderFulfillmentService {

private InventoryService inventoryService;
private PaymentQueryService paymentQueryService;

@Transactional
public void handleOrderPaymentConfirmed(String orderId) {
Order order = orderRepository.findById(orderId);

// 1. 检查支付是否真实完成
if (!paymentQueryService.isPaymentValid(orderId)) {
throw new BusinessRuleViolation("支付未验证通过");
}

// 2. 锁定库存(调用库存上下文)
boolean locked = inventoryService.reserveInventory(order.getItems());
if (!locked) {
throw new BusinessRuleViolation("库存不足");
}

// 3. 确认订单
order.confirmPayment();
orderRepository.save(order); // 触发事件发布
}
}

仓储接口(Repository)

定义在领域层,实现放在基础设施层:

1
2
3
4
5
6
// 领域层接口
public interface OrderRepository {
Order findById(String orderId);
void save(Order order);
List<Order> findByCustomer(String customerId);
}

工厂封装(Factory)

封装复杂创建逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class OrderFactory {

public static Order createFromCart(
String orderId,
Customer customer,
ShoppingCart cart,
Address address) {

// 校验用户是否可下单
if (!customer.isActive()) {
throw new BusinessRuleViolation("用户被禁用");
}

// 校验购物车非空
if (cart.getItems().isEmpty()) {
throw new BusinessRuleViolation("购物车为空");
}

return Order.createFromCart(orderId, customer.getId(), cart.getItems(), address);
}
}

事件驱动的订单状态机设计

  • 订单的状态流转不再由“硬编码 if-else”控制,而是通过 监听领域事件 触发状态变更。

  • 使用 状态机(State Machine) 模型来管理 OrderStatus 的合法转换。

  • 所有状态变更由 领域事件驱动,保证逻辑集中、可追溯、可扩展。

当前状态 → 新状态 触发事件 是否允许
PENDING → PAID OrderPaidEvent
PENDING → CANCELLED OrderCancelledEvent
PAID → SHIPPED OrderShippedEvent
PAID → CANCELLED OrderRefundedEvent 是(退款后取消)

状态机实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@Component
public class OrderStateMachine {

// 定义合法的状态转移
private final Map<OrderStatus, List<OrderStatus>> transitions = Map.of(
OrderStatus.PENDING, Arrays.asList(OrderStatus.PAID, OrderStatus.CANCELLED),
OrderStatus.PAID, Arrays.asList(OrderStatus.SHIPPED, OrderStatus.CANCELLED)
// SHIPPED 没有出边 → 不可变更
);

public boolean canTransition(OrderStatus from, OrderStatus to) {
if (from == null || to == null) return false;
List<OrderStatus> allowed = transitions.getOrDefault(from, Collections.emptyList());
return allowed.contains(to);
}

// 执行状态变更(由事件处理器调用)
public void applyEvent(Order order, DomainEvent event) {
OrderStatus newStatus = null;

if (event instanceof OrderPaidEvent) {
newStatus = OrderStatus.PAID;
} else if (event instanceof OrderShippedEvent) {
newStatus = OrderStatus.SHIPPED;
} else if (event instanceof OrderCancelledEvent || event instanceof OrderRefundedEvent) {
newStatus = OrderStatus.CANCELLED;
}

if (newStatus != null) {
if (canTransition(order.getStatus(), newStatus)) {
order.setStatusInternal(newStatus); // 注意:内部私有方法,不暴露给外部,避免外部随意修改状态
} else {
throw new BusinessRuleViolation(
"非法状态转移: " + order.getStatus() + " → " + newStatus);
}
}
}
}

事件处理器(Event Handlers)

使用 Spring 的事件机制或自定义事件总线监听领域事件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Component
@RequiredArgsConstructor
public class OrderEventHandlers {

private final OrderRepository orderRepository;
private final OrderStateMachine stateMachine;

@EventListener
public void handle(OrderPaidEvent event) {
Order order = orderRepository.findById(event.getOrderId());
stateMachine.applyEvent(order, event);
orderRepository.save(order);
}

@EventListener
public void handle(OrderShippedEvent event) {
Order order = orderRepository.findById(event.getOrderId());
stateMachine.applyEvent(order, event);
orderRepository.save(order);
}

@EventListener
public void handle(OrderCancelledEvent event) {
Order order = orderRepository.findById(event.getOrderId());
stateMachine.applyEvent(order, event);
orderRepository.save(order);
}
}

支持事件溯源

1
2
3
4
5
6
7
8
// 伪代码:从事件重建订单
public Order reconstituteFromHistory(String orderId, List<DomainEvent> events) {
Order order = new Order();
for (DomainEvent event : events) {
event.applyTo(order); // 每个事件自己知道如何影响订单
}
return order;
}

防腐层(支付、库存)

在限界上下文之间建立翻译与隔离层,防止外部概念“侵蚀”本领域的纯洁性。 外部系统(如支付系统、库存系统)

外部系统(支付宝)
↓ HTTP 回调
[ 支付回调 Controller ]

[ AlipayAclAdapter ] → 验签 + 校验

[ PaymentEventMapper ] → 映射为 OrderPaidEvent

[ Domain Event Bus ]
├──→ [OrderEventHandlers] → 触发 Order 状态机
└──→ [OrderStateMachine] → 更新订单状态

[OrderRepository] → 数据持久化

  1. 外部支付回调 DTO(来自支付宝)
    1
    2
    3
    4
    5
    public class AlipayCallback {
    private String tradeNo;
    private String outTradeNo; // 我方订单号
    private String tradeStatus;
    }
  2. 防腐层适配器(ACL Adapter)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    @Component
    public class AlipayAclAdapter {

    private final PaymentEventMapper eventMapper;
    private final DomainEventPublisher eventPublisher;

    public void handleCallback(AlipayCallback callback) {
    // 1. 校验签名(安全)
    if (!verifySignature(callback)) {
    throw new SecurityException("非法回调");
    }

    // 2. 映射为本地下游事件
    DomainEvent domainEvent = eventMapper.toDomainEvent(callback);

    // 3. 发布事件,驱动本域逻辑
    if (domainEvent != null) {
    eventPublisher.publish(domainEvent);
    }
    }

    private boolean verifySignature(AlipayCallback callback) {
    // 调用支付宝 SDK 验签
    return AlipaySignature.rsaCheckV2(...) == true;
    }
    }
  3. 事件映射器(Mapping Logic)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Component
    public class PaymentEventMapper {

    public DomainEvent toDomainEvent(AlipayCallback callback) {
    String orderId = callback.getOutTradeNo();

    switch (callback.getTradeStatus()) {
    case "TRADE_SUCCESS":
    case "TRADE_FINISHED":
    return new OrderPaidEvent(orderId, Money.of(999, "CNY")); // 可结合查询订单金额
    case "TRADE_CLOSED":
    return new OrderRefundedEvent(orderId);
    default:
    return null;
    }
    }
    }

应用层(Service)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Service
@Transactional
public class OrderService {

@Autowired
private OrderRepository orderRepository;

@Autowired
private PaymentService paymentService;

@Autowired
private DomainEventPublisher eventPublisher;

public OrderId createOrder(CreateOrderCommand command) {
Order order = new Order(command.getOrderId(), command.getAmount());
order.create();
orderRepository.save(order);

// 发布事件
order.getEvents().forEach(eventPublisher::publish);
return order.getId();
}

public void payOrder(PayOrderCommand command) {
Order order = orderRepository.findById(command.getOrderId())
.orElseThrow(() -> new BusinessException("Order not found"));

// 先调用支付
PaymentResult result = paymentService.processPayment(
new PaymentRequest(order.getAmount(), command.getCardToken())
);

if (result.getStatus() == PaymentStatus.SUCCESS) {
order.pay();
orderRepository.save(order);
order.getEvents().forEach(eventPublisher::publish);
} else {
throw new BusinessException("Payment failed");
}
}
}

接口层(Controller)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RestController
@RequestMapping("/api/orders")
public class OrderController {

@Autowired
private OrderService orderService;

@PostMapping
public ResponseEntity<String> createOrder(@RequestBody OrderRequestDTO dto) {
OrderId id = orderService.createOrder(new CreateOrderCommand(dto.getAmount()));
return ResponseEntity.ok(id.getValue());
}

@PostMapping("/{orderId}/pay")
public ResponseEntity<String> payOrder(@PathVariable String orderId, @RequestBody PaymentRequestDTO dto) {
orderService.payOrder(new PayOrderCommand(new OrderId(orderId), dto.getCardToken()));
return ResponseEntity.ok("Paid");
}
}