← 返回首页
🎯

DDD与微服务

📂 architecture ⏱ 2 min 358 words

DDD与微服务

限界上下文作为服务边界

DDD 的限界上下文是划分微服务的最佳指导。每个微服务对应一个限界上下文,拥有独立的领域模型和数据存储。

电商系统微服务划分:

┌─────────────────────────────────────────────────────────────────┐
│                        电商平台                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐             │
│  │  订单服务    │  │  商品服务    │  │  库存服务    │             │
│  │ (Order)     │  │ (Product)   │  │ (Inventory) │             │
│  │             │  │             │  │             │             │
│  │ - Order     │  │ - Product   │  │ - Stock     │             │
│  │ - OrderItem │  │ - Category  │  │ - Warehouse │             │
│  └─────────────┘  └─────────────┘  └─────────────┘             │
│         │                │                │                     │
│         └────────────────┼────────────────┘                     │
│                          │                                      │
│                    事件驱动通信                                   │
│                                                                 │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐             │
│  │  支付服务    │  │  用户服务    │  │  物流服务    │             │
│  │ (Payment)   │  │ (User)      │  │ (Shipping)  │             │
│  │             │  │             │  │             │             │
│  │ - Payment   │  │ - Customer  │  │ - Shipment  │             │
│  │ - Refund    │  │ - Address   │  │ - Tracking  │             │
│  └─────────────┘  └─────────────┘  └─────────────┘             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

服务间通信

微服务间通信主要有同步调用和异步事件两种方式。

// 同步调用:使用 Feign Client
@FeignClient(name = "inventory-service")
public interface InventoryClient {
    
    @PostMapping("/api/inventory/check")
    CheckResult checkAvailability(@RequestBody CheckRequest request);
    
    @PostMapping("/api/inventory/reserve")
    void reserve(@RequestBody ReserveRequest request);
}

// 异步事件:使用消息队列
@Component
public class OrderEventPublisher {
    
    private final KafkaTemplate<String, Object> kafkaTemplate;
    
    public void publishOrderCreated(Order order) {
        OrderCreatedEvent event = new OrderCreatedEvent(
            order.getId().getValue(),
            order.getCustomerId().getValue(),
            order.getItems().stream()
                .map(item -> new OrderItemDto(
                    item.getProductId().getValue(),
                    item.getQuantity()
                ))
                .collect(Collectors.toList())
        );
        
        kafkaTemplate.send("order-events", event);
    }
}

// 事件消费者
@Component
public class InventoryEventConsumer {
    
    @KafkaListener(topics = "order-events")
    public void handleOrderCreated(OrderCreatedEvent event) {
        // 检查库存
        for (OrderItemDto item : event.getItems()) {
            if (!inventoryService.checkAvailability(
                    item.getProductId(), item.getQuantity())) {
                throw new InsufficientInventoryException();
            }
        }
        
        // 预留库存
        inventoryService.reserve(event.getOrderId(), event.getItems());
    }
}

数据一致性

微服务间的数据一致性通常采用最终一致性模式。

// Saga 模式:分布式事务
@Component
public class OrderSaga {
    
    private final OrderService orderService;
    private final InventoryClient inventoryClient;
    private final PaymentClient paymentClient;
    
    @SagaStep(order = 1, compensation = "cancelOrder")
    public void createOrder(CreateOrderCommand command) {
        orderService.createOrder(command);
    }
    
    @SagaStep(order = 2, compensation = "releaseInventory")
    public void reserveInventory(String orderId) {
        Order order = orderService.findById(orderId);
        inventoryClient.reserve(orderId, order.getItems());
    }
    
    @SagaStep(order = 3, compensation = "cancelPayment")
    public void processPayment(String orderId) {
        Order order = orderService.findById(orderId);
        paymentClient.charge(orderId, order.calculateTotal());
    }
    
    // 补偿操作
    public void cancelOrder(String orderId) {
        orderService.cancel(orderId);
    }
    
    public void releaseInventory(String orderId) {
        inventoryClient.release(orderId);
    }
    
    public void cancelPayment(String orderId) {
        paymentClient.refund(orderId);
    }
}

服务划分原则

DDD 的限界上下文为微服务划分提供了清晰的指导原则。