本文目录导读:

下面通过一个完整的“订单履约”业务场景,展示Java领域驱动设计(DDD)的落地过程,我们围绕订单、商品、库存、支付四个核心子域展开,重点在订单子域演示战术建模。
背景与业务愿景
场景:一个电商平台,用户下单后系统自动进行库存锁定、支付、履约,需要支撑高并发订单流程,后续可扩展至多仓发货、预售、分拆订单等复杂场景。
战略设计(限界上下文映射)
核心子域划分
| 限界上下文 | 优先级 | 关键职责 |
|---|---|---|
| 订单(Order Context) | 核心域 | 订单状态机、生命周期、订单履行的聚合根 |
| 商品(Catalog Context) | 支撑域 | 商品信息、SKU、价格 |
| 库存(Inventory Context) | 支撑域 | 库存扣减、锁定、回滚 |
| 支付(Payment Context) | 通用域 | 支付预授权、回调处理 |
| 履约(Fulfillment Context) | 核心域 | 仓配调度(可扩展) |
上下文映射(COLA风格)
// 上下文间通过防腐层(Anti-Corruption Layer)通信
public class OrderContext implements ContextBoundary {
private final InventoryServiceClient inventoryClient;
private final PaymentServiceClient paymentClient;
private final DomainEventBus eventBus;
public OrderContext(...) {
// 注入各类客户端
}
public Order createOrder(CreateOrderCommand cmd) {
// 1. 校验商品(通过ACL调用Catalog)
// 2. 锁定库存(通过ACL调用Inventory)
// 3. 创建订单聚合
// 4. 发布OrderCreatedEvent
}
}
战术建模(订单子域)
领域模型核心类
聚合根:Order(订单)
@Entity
@AggregateRoot
public class Order {
@EmbeddedId
private OrderId orderId;
@Embedded
private CustomerId customerId; // 值对象
private OrderStatus status; // 枚举:INITIATED, CONFIRMED, PAID, FULFILLING, COMPLETED, CANCELLED
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private List<OrderLine> orderLines; // 订单项集合
@Embedded
private Address shippingAddress; // 值对象
private ZonedDateTime createdAt;
private ZonedDateTime updatedAt;
// 领域事件列表(非持久化)
@Transient
private List<DomainEvent> domainEvents = new ArrayList<>();
// 构造函数(业务创建)
public static Order create(CreateOrderCommand cmd) {
Order order = new Order();
order.orderId = new OrderId(UUID.randomUUID());
order.customerId = new CustomerId(cmd.customerId());
order.status = OrderStatus.INITIATED;
order.createdAt = ZonedDateTime.now();
cmd.lineItems().forEach(item -> {
order.addLine(
new ProductId(item.productId()),
item.quantity(),
new Money(item.unitPrice()),
new Sku(item.sku())
);
});
return order;
}
// 领域行为:确认订单
public void confirm(InventoryLockResult lockResult) {
if (!canConfirm()) {
throw new OrderStateException("Order cannot be confirmed in state: " + status);
}
this.status = OrderStatus.CONFIRMED;
this.updatedAt = ZonedDateTime.now();
this.addDomainEvent(new OrderConfirmedEvent(orderId, Instant.now()));
}
// 领域行为:支付成功
public void markPaid(PaymentReceipt paymentReceipt) {
if (status != OrderStatus.CONFIRMED) {
throw new OrderStateException("Only confirmed orders can be paid");
}
this.status = OrderStatus.PAID;
this.updatedAt = ZonedDateTime.now();
this.addDomainEvent(new OrderPaidEvent(orderId, paymentReceipt.transactionId()));
}
// 领域行为:取消订单(带回滚逻辑)
public void cancel(String reason) {
if (status.isCancellable()) {
this.status = OrderStatus.CANCELLED;
this.cancelReason = reason;
this.addDomainEvent(new OrderCancelledEvent(orderId, reason, this.orderLines));
}
}
// 领域行为:验证库存是否充足
private boolean canConfirm() {
return this.status == OrderStatus.INITIATED
&& this.orderLines.stream().allMatch(OrderLine::hasStock);
}
// 获取领域事件
public List<DomainEvent> getDomainEvents() { return List.copyOf(domainEvents); }
public void clearDomainEvents() { this.domainEvents.clear(); }
private void addDomainEvent(DomainEvent event) {
this.domainEvents.add(event);
}
}
实体:OrderLine(订单行)
@Entity
public class OrderLine {
@Id private Long id;
private ProductId productId;
private Money unitPrice;
private Quantity quantity;
private boolean stockLocked = false;
public void lockStock() {
this.stockLocked = true;
}
public Money total() {
return unitPrice.multiply(quantity.value());
}
}
值对象:Money(金额)
@Embeddable
public class Money {
private BigDecimal amount;
private Currency currency;
public Money(BigDecimal amount, Currency currency) {
if (amount.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("Money cannot be negative");
}
this.amount = amount;
this.currency = currency;
}
protected Money() {} // JPA uses reflection
public Money add(Money other) {
requireSameCurrency(other);
return new Money(amount.add(other.amount), currency);
}
public Money multiply(int factor) {
return new Money(amount.multiply(BigDecimal.valueOf(factor)), currency);
}
private void requireSameCurrency(Money other) {
if (!currency.equals(other.currency)) {
throw new CurrencyMismatchException("Currencies do not match");
}
}
}
领域服务:InventoryLockService
@Service
public class InventoryLockService {
private final InventoryClient inventoryClient; // 防腐层
@Transactional
public InventoryLockResult lockStockForOrder(Order order) {
// 1. 查询所有需要锁定的SKU
Map<String, Integer> skuQuantities = order.getOrderLines().stream()
.collect(Collectors.toMap(...));
// 2. 调用库存服务锁定库存
List<InventoryLockResult.Item> results = skuQuantities.entrySet().stream()
.map(entry -> inventoryClient.lockSku(entry.getKey(), entry.getValue()))
.toList();
// 3. 如果任何一项锁定失败,回滚所有锁定
if (results.stream().anyMatch(Item::failed)) {
results.stream().map(Item::lockId).forEach(inventoryClient::unlock);
throw new InventoryLockException("库存不足");
}
// 4. 标记订单行库存已锁定
order.getOrderLines().forEach(OrderLine::lockStock);
return new InventoryLockResult(results);
}
}
领域事件与消息
// 领域事件基类
public interface DomainEvent {
Instant occurredOn();
}
// 具体事件
public record OrderConfirmedEvent(OrderId orderId, Instant occurredOn) implements DomainEvent {}
public record OrderPaidEvent(OrderId orderId, String transactionId) implements DomainEvent {}
public record OrderCancelledEvent(OrderId orderId, String reason, List<OrderLine> lines) implements DomainEvent {}
应用层(用例驱动)
@Service
@UseCase
public class CreateOrderUseCase implements CommandHandler<CreateOrderCommand, OrderDTO> {
private final OrderRepository orderRepository;
private final OrderFactory orderFactory;
private final InventoryLockService inventoryLockService;
private final EventPublisher eventPublisher;
@Override
@Transactional
public OrderDTO handle(CreateOrderCommand cmd) {
// 1. 创建订单(聚合根)
Order order = orderFactory.createFrom(cmd);
// 2. 锁定库存(领域服务)
InventoryLockResult lockResult = inventoryLockService.lockStockForOrder(order);
// 3. 确认订单(领域行为)
order.confirm(lockResult);
// 4. 保存订单
orderRepository.save(order);
// 5. 发布领域事件(最终一致性)
order.getDomainEvents().forEach(eventPublisher::publish);
return OrderMapper.toDTO(order);
}
}
基础设施层(仓储实现)
@Repository
public class JpaOrderRepository implements OrderRepository {
@PersistenceContext private EntityManager em;
private final EventBus eventBus;
@Override
@Transactional
public void save(Order order) {
em.persist(order);
// 发布事件到消息队列
order.getDomainEvents().forEach(eventBus::publish);
}
@Override
public Optional<Order> findById(OrderId id) {
Order order = em.find(Order.class, id);
if (order != null) {
// 加载时获取领域事件(可广播给其他订阅者)
order.getDomainEvents().forEach(eventBus::publish);
}
return Optional.ofNullable(order);
}
}
防腐层(Anti-Corruption Layer)
@Component
public class InventoryServiceClient {
private final RestTemplate restTemplate;
public InventoryLockResponse lockSku(String sku, int quantity) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Map<String, Object>> request = new HttpEntity<>(
Map.of("sku", sku, "quantity", quantity), headers
);
try {
ResponseEntity<InventoryLockResponse> response =
restTemplate.postForEntity("http://inventory-service/api/v1/lock", request, InventoryLockResponse.class);
return response.getBody();
} catch (HttpClientErrorException e) {
if (e.getStatusCode() == HttpStatus.CONFLICT) {
return InventoryLockResponse.failed();
}
throw new RemoteServiceException("Inventory service error", e);
}
}
}
关键设计模式
状态机模式(Order Status)
public enum OrderStatus {
INITIATED {
@Override
public boolean canTransitionTo(OrderStatus target) {
return target == CONFIRMED || target == CANCELLED;
}
},
CONFIRMED {
@Override
public boolean canTransitionTo(OrderStatus target) {
return target == PAID || target == CANCELLED;
}
},
PAID {
@Override
public boolean canTransitionTo(OrderStatus target) {
return target == FULFILLING || target == REFUND_PENDING;
}
},
// ... 其他状态
;
public abstract boolean canTransitionTo(OrderStatus target);
}
领域事件追踪
// 使用Spring @TransactionalEventListener 保证事件边界
@Component
public class OrderEventListener {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleOrderConfirmed(OrderConfirmedEvent event) {
// 发送通知
notificationService.notifyOrderConfirmed(event.orderId());
}
}
测试策略
领域模型单元测试
class OrderTest {
@Test
void shouldConfirmOrderWhenStockAvailable() {
Order order = Order.create(orderCommand);
order.confirm(lockResult.success());
assertEquals(OrderStatus.CONFIRMED, order.getStatus());
assertEquals(1, order.getDomainEvents().size());
assertTrue(order.getDomainEvents().get(0) instanceof OrderConfirmedEvent);
}
@Test
void shouldRejectConfirmWhenNoStock() {
Order order = Order.create(orderCommand);
assertThrows(InventoryLockException.class,
() -> order.confirm(lockResult.failure()));
}
}
应用用例测试(带Mock)
@SpringBootTest
@Transactional
class CreateOrderUseCaseTest {
@MockBean InventoryServiceClient inventoryClient;
@Autowired CreateOrderUseCase createOrderUseCase;
@Test
void shouldCreateOrderSuccessfully() {
// 模拟库存锁定成功
when(inventoryClient.lockSku(anyString(), anyInt()))
.thenReturn(InventoryLockResponse.success());
CreateOrderCommand command = new CreateOrderCommand(
"customer123",
List.of(new LineItem("sku1", 2, "10.00"))
);
OrderDTO dto = createOrderUseCase.handle(command);
assertNotNull(dto.orderId());
assertEquals("CONFIRMED", dto.status());
// Verify event published
verify(eventPublisher, times(1)).publish(any(OrderConfirmedEvent.class));
}
}
架构演进建议
| 阶段 | 关注点 | 具体措施 |
|---|---|---|
| 初期 | 快速验证 | 单模块+简单分层,核心领域逻辑放domain包内 |
| 中期 | 拆分上下文 | 订单、库存拆分独立模块,使用OpenFeign/HTTP通信 |
| 后期 | 性能优化 | 引入CQRS(读模型使用Elasticsearch)、引入Event Sourcing存储 |
| 扩展 | 多仓支持 | 履约上下文单独拆分为微服务,实现分仓路由 |
这个案例展示了:
✅ 战略建模:合理划分限界上下文
✅ 核心模型:聚合根、实体、值对象、领域服务
✅ 战术落地:状态机、领域事件、防腐层
✅ 测试策略:聚焦业务规则的单元测试与集成测试
✅ 演进路径:支持逐步微服务化
它将DDD与Spring Boot、JPA、EventBus等Java生态结合,适合直接作为生产项目的基础模板。