领域驱动设计(DDD):2025 实战指南
领域驱动设计DDD2025 实战指南我是 Alex一个在 CSDN 写 Java 架构思考的暖男。看到新手博主写技术踩坑记录总会留言这个 debug 思路很 solid下次试试加个 circuit breaker 会更优雅。我的文章里从不说空话每个架构图都经过生产环境验证。对了别叫我大神喊我 Alex 就好。一、领域驱动设计的核心概念领域驱动设计DDD是一种软件开发方法它强调将业务领域的知识和模型作为设计的核心。通过深入理解业务领域可以构建更加符合业务需求的软件系统。1.1 核心概念领域Domain业务领域包含业务规则和业务逻辑子域Subdomain领域的子部分如核心域、支撑域和通用域限界上下文Bounded Context领域模型的边界每个上下文有自己的模型实体Entity具有唯一标识的对象值对象Value Object没有唯一标识的对象通过属性值来区分聚合Aggregate由多个相关对象组成的集合有一个根实体领域服务Domain Service实现领域逻辑的服务仓储Repository负责持久化聚合的接口工厂Factory负责创建复杂对象的组件1.2 DDD 的重要性业务对齐确保软件系统与业务需求保持一致复杂性管理通过限界上下文和聚合来管理复杂的业务逻辑可维护性清晰的领域模型提高系统的可维护性可扩展性良好的领域模型便于系统扩展二、领域模型设计2.1 实体设计Entity public class Order implements AggregateRootLong { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; private String orderNumber; private OrderStatus status; private LocalDateTime createdAt; private LocalDateTime updatedAt; Embedded private CustomerInfo customerInfo; OneToMany(mappedBy order, cascade CascadeType.ALL, orphanRemoval true) private ListOrderItem items new ArrayList(); // 业务方法 public void addItem(Product product, int quantity) { OrderItem item new OrderItem(this, product, quantity); items.add(item); updateStatus(); } public void removeItem(OrderItem item) { items.remove(item); updateStatus(); } public void updateStatus() { if (items.isEmpty()) { status OrderStatus.EMPTY; } else { status OrderStatus.PENDING; } } // getters and setters }2.2 值对象设计Embeddable public class CustomerInfo { private String name; private String email; private String phone; // 业务规则 public boolean isValidEmail() { return email.matches([a-zA-Z0-9._%-][a-zA-Z0-9.-]\\.[a-zA-Z]{2,}); } // 重写 equals 和 hashCode Override public boolean equals(Object o) { if (this o) return true; if (o null || getClass() ! o.getClass()) return false; CustomerInfo that (CustomerInfo) o; return Objects.equals(name, that.name) Objects.equals(email, that.email) Objects.equals(phone, that.phone); } Override public int hashCode() { return Objects.hash(name, email, phone); } // getters and setters }2.3 聚合设计public class OrderAggregate { private Order order; public OrderAggregate(Order order) { this.order order; } public void addItem(Product product, int quantity) { order.addItem(product, quantity); } public void removeItem(OrderItem item) { order.removeItem(item); } public void placeOrder() { if (order.getItems().isEmpty()) { throw new IllegalStateException(Cannot place an empty order); } order.setStatus(OrderStatus.PLACED); } public Order getOrder() { return order; } }三、领域服务3.1 领域服务设计Service public class OrderDomainService { private final OrderRepository orderRepository; private final ProductRepository productRepository; Autowired public OrderDomainService(OrderRepository orderRepository, ProductRepository productRepository) { this.orderRepository orderRepository; this.productRepository productRepository; } public Order placeOrder(OrderCreateCommand command) { // 创建订单 Order order new Order(); order.setOrderNumber(generateOrderNumber()); order.setCustomerInfo(command.getCustomerInfo()); // 添加订单项目 for (OrderItemCommand itemCommand : command.getItems()) { Product product productRepository.findById(itemCommand.getProductId()) .orElseThrow(() - new ProductNotFoundException(itemCommand.getProductId())); order.addItem(product, itemCommand.getQuantity()); } // 验证订单 validateOrder(order); // 保存订单 return orderRepository.save(order); } private void validateOrder(Order order) { if (order.getItems().isEmpty()) { throw new IllegalStateException(Order cannot be empty); } // 检查库存 for (OrderItem item : order.getItems()) { Product product item.getProduct(); if (product.getStock() item.getQuantity()) { throw new InsufficientStockException(product.getId(), item.getQuantity()); } } } private String generateOrderNumber() { return ORD System.currentTimeMillis(); } }3.2 应用服务Service public class OrderApplicationService { private final OrderDomainService orderDomainService; private final InventoryService inventoryService; Autowired public OrderApplicationService(OrderDomainService orderDomainService, InventoryService inventoryService) { this.orderDomainService orderDomainService; this.inventoryService inventoryService; } Transactional public OrderDTO createOrder(OrderCreateRequest request) { // 转换请求为命令 OrderCreateCommand command new OrderCreateCommand( request.getCustomerInfo(), request.getItems().stream() .map(item - new OrderItemCommand(item.getProductId(), item.getQuantity())) .collect(Collectors.toList()) ); // 调用领域服务 Order order orderDomainService.placeOrder(command); // 扣减库存 for (OrderItem item : order.getItems()) { inventoryService.deductStock(item.getProduct().getId(), item.getQuantity()); } // 转换为 DTO return convertToDTO(order); } private OrderDTO convertToDTO(Order order) { // 转换逻辑 } }四、仓储设计4.1 仓储接口public interface OrderRepository extends JpaRepositoryOrder, Long { OptionalOrder findByOrderNumber(String orderNumber); ListOrder findByCustomerInfoEmail(String email); ListOrder findByStatus(OrderStatus status); } public interface ProductRepository extends JpaRepositoryProduct, Long { OptionalProduct findByName(String name); ListProduct findByCategory(String category); }4.2 仓储实现Repository public class OrderRepositoryImpl implements OrderRepository { private final EntityManager entityManager; Autowired public OrderRepositoryImpl(EntityManager entityManager) { this.entityManager entityManager; } Override public OptionalOrder findById(Long id) { return Optional.ofNullable(entityManager.find(Order.class, id)); } Override public Order save(Order order) { if (order.getId() null) { entityManager.persist(order); return order; } else { return entityManager.merge(order); } } Override public OptionalOrder findByOrderNumber(String orderNumber) { TypedQueryOrder query entityManager.createQuery( SELECT o FROM Order o WHERE o.orderNumber :orderNumber, Order.class); query.setParameter(orderNumber, orderNumber); return query.getResultList().stream().findFirst(); } // 其他方法实现 }五、限界上下文5.1 限界上下文划分订单上下文处理订单的创建、修改、查询等库存上下文处理库存的管理和更新支付上下文处理支付的处理和状态更新客户上下文处理客户信息的管理5.2 上下文映射合作关系Partnership两个上下文之间有明确的合作关系共享内核Shared Kernel两个上下文共享部分模型客户-供应商Customer-Supplier一个上下文是另一个的客户防腐层Anti-Corruption Layer防止一个上下文的模型影响另一个// 防腐层示例 Service public class OrderServiceAdapter { private final InventoryClient inventoryClient; Autowired public OrderServiceAdapter(InventoryClient inventoryClient) { this.inventoryClient inventoryClient; } public void deductInventory(Long productId, int quantity) { InventoryRequest request new InventoryRequest(productId, quantity); inventoryClient.deductStock(request); } public boolean checkInventory(Long productId, int quantity) { InventoryCheckRequest request new InventoryCheckRequest(productId, quantity); InventoryCheckResponse response inventoryClient.checkStock(request); return response.isAvailable(); } }六、领域事件6.1 领域事件设计public class OrderPlacedEvent { private final String orderNumber; private final LocalDateTime timestamp; private final CustomerInfo customerInfo; private final ListOrderItemEvent items; public OrderPlacedEvent(String orderNumber, CustomerInfo customerInfo, ListOrderItemEvent items) { this.orderNumber orderNumber; this.timestamp LocalDateTime.now(); this.customerInfo customerInfo; this.items items; } // getters } public class OrderItemEvent { private final Long productId; private final String productName; private final int quantity; private final BigDecimal price; public OrderItemEvent(Long productId, String productName, int quantity, BigDecimal price) { this.productId productId; this.productName productName; this.quantity quantity; this.price price; } // getters }6.2 事件发布与订阅Service public class OrderDomainEventPublisher { private final ApplicationEventPublisher eventPublisher; private final KafkaTemplateString, Object kafkaTemplate; Autowired public OrderDomainEventPublisher(ApplicationEventPublisher eventPublisher, KafkaTemplateString, Object kafkaTemplate) { this.eventPublisher eventPublisher; this.kafkaTemplate kafkaTemplate; } public void publishOrderPlacedEvent(Order order) { ListOrderItemEvent items order.getItems().stream() .map(item - new OrderItemEvent( item.getProduct().getId(), item.getProduct().getName(), item.getQuantity(), item.getProduct().getPrice() )) .collect(Collectors.toList()); OrderPlacedEvent event new OrderPlacedEvent( order.getOrderNumber(), order.getCustomerInfo(), items ); // 发布本地事件 eventPublisher.publishEvent(event); // 发布到消息队列 kafkaTemplate.send(order-placed, event); } } // 事件监听器 Component public class OrderPlacedEventListener { private final InventoryService inventoryService; private final NotificationService notificationService; Autowired public OrderPlacedEventListener(InventoryService inventoryService, NotificationService notificationService) { this.inventoryService inventoryService; this.notificationService notificationService; } EventListener public void handleOrderPlacedEvent(OrderPlacedEvent event) { // 扣减库存 for (OrderItemEvent item : event.getItems()) { inventoryService.deductStock(item.getProductId(), item.getQuantity()); } // 发送通知 notificationService.sendOrderConfirmation(event.getCustomerInfo().getEmail(), event.getOrderNumber()); } }七、DDD 实践技巧7.1 领域专家合作定期与领域专家交流了解业务规则和需求领域建模工作坊组织团队和领域专家一起进行领域建模事件风暴通过事件风暴来发现领域事件和聚合7.2 战术设计技巧保持实体和值对象的纯净只包含领域逻辑使用领域服务处理跨实体的业务逻辑避免实体之间的直接依赖合理设计聚合确保聚合的大小适中职责清晰使用仓储模式将持久化逻辑与领域逻辑分离7.3 战略设计技巧识别核心域优先投资核心域的设计和实现合理划分限界上下文根据业务边界和团队结构划分设计上下文映射明确上下文之间的关系和集成方式使用防腐层防止不同上下文之间的模型污染八、生产环境案例分析8.1 案例一电商平台 DDD 实践某电商平台通过实施 DDD成功重构了核心订单系统。主要措施包括识别核心域订单、库存、支付划分限界上下文订单上下文、库存上下文、支付上下文设计聚合订单聚合、产品聚合、库存聚合实现领域服务订单服务、库存服务、支付服务使用领域事件订单创建事件、库存更新事件、支付完成事件实施 DDD 后系统的可维护性和可扩展性显著提高业务逻辑更加清晰团队协作更加高效。8.2 案例二金融系统 DDD 架构某银行通过 DDD 设计构建了新一代核心 banking 系统。主要措施包括识别核心子域账户、交易、风险设计限界上下文账户上下文、交易上下文、风险上下文实现领域模型账户实体、交易实体、风险评估服务使用事件驱动架构通过领域事件实现上下文之间的通信构建领域服务账户服务、交易服务、风险服务实施 DDD 后系统的业务对齐度提高能够更快地响应业务需求变化同时保持系统的稳定性和可扩展性。九、常见误区与解决方案9.1 过度设计问题领域模型过于复杂增加了维护成本解决方案保持模型简洁只包含必要的业务逻辑9.2 贫血模型问题实体和值对象只包含数据没有业务逻辑解决方案将业务逻辑放入实体和值对象中使用领域服务处理跨实体的业务逻辑9.3 限界上下文划分不合理问题限界上下文过大或过小影响系统设计解决方案根据业务边界和团队结构合理划分限界上下文9.4 缺乏领域专家参与问题领域模型与业务需求不一致解决方案定期与领域专家交流组织领域建模工作坊十、总结与展望领域驱动设计是一种强大的软件开发方法它强调将业务领域的知识和模型作为设计的核心。通过深入理解业务领域可以构建更加符合业务需求的软件系统。在微服务架构时代DDD 更是发挥着重要作用。通过限界上下文的划分可以更好地设计微服务的边界提高系统的可维护性和可扩展性。记住DDD 不是银弹它需要团队的共同努力和持续实践。这其实可以更优雅一点。别叫我大神叫我 Alex 就好。如果你在 DDD 实践中遇到了问题欢迎在评论区留言我会尽力为你提供建设性的建议。