本文目录导读:

下面通过一个完整的“订单履约”业务场景,展示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生态结合,适合直接作为生产项目的基础模板。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播