领域驱动设计(DDD)实战:构建清晰边界的企业级应用
领域驱动设计DDD实战构建清晰边界的企业级应用一、DDD概述1.1 什么是DDD领域驱动设计Domain-Driven DesignDDD是一种软件开发方法论强调以业务领域为核心将业务逻辑放在核心位置通用语言开发团队与业务专家使用统一语言限界上下文划分清晰的问题边界聚合设计将相关实体和值对象组织在一起1.2 DDD核心概念┌─────────────────────────────────────────────────────────────────┐ │ 限界上下文 (Bounded Context) │ ├─────────────────────────────────────────────────────────────────┤ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ 聚合 │ │ 聚合 │ │ 聚合 │ │ │ │ (Aggregate)│ │ │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ 实体 │ │ 实体 │ │ │ │ (Entity) │ │ │ │ │ └─────────────┘ └─────────────┘ │ │ │ │ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ 值对象 │ │ 值对象 │ │ │ │(Value Object)│ │ │ │ │ └─────────────┘ └─────────────┘ │ └─────────────────────────────────────────────────────────────────┘1.3 DDD分层架构┌────────────────────────────────────────────────────────────────┐ │ 用户界面层 (UI Layer) │ ├────────────────────────────────────────────────────────────────┤ │ 应用层 (Application Layer) │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ 应用服务 │ │ DTO │ │ │ │ (Application │ │ (Data Transfer │ │ │ │ Services) │ │ Objects) │ │ │ └──────────────────┘ └──────────────────┘ │ ├────────────────────────────────────────────────────────────────┤ │ 领域层 (Domain Layer) │ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ │ │聚合根 │ │ 实体 │ │值对象 │ │领域服务│ │仓储接口│ │ │ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘ │ ├────────────────────────────────────────────────────────────────┤ │ 基础设施层 (Infrastructure Layer) │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ 仓储实现 │ │ 外部服务 │ │ │ │ (Repository │ │ (External │ │ │ │ Implementation) │ │ Services) │ │ │ └──────────────────┘ └──────────────────┘ │ └────────────────────────────────────────────────────────────────┘二、聚合设计2.1 聚合根/** * 订单聚合根 - 订单是订单系统的核心实体 */ public class Order extends AggregateRoot { private OrderId id; private CustomerId customerId; private Money totalAmount; private OrderStatus status; private ListOrderItem items; private ShippingAddress shippingAddress; private PaymentInfo paymentInfo; // 私有构造函数通过工厂方法创建 private Order(OrderId id, CustomerId customerId, ListOrderItem items) { this.id id; this.customerId customerId; this.items new ArrayList(items); this.totalAmount calculateTotal(); this.status OrderStatus.PENDING; this.createdAt LocalDateTime.now(); // 添加领域事件 registerEvent(new OrderCreatedEvent(this.id, this.customerId, this.totalAmount)); } // 工厂方法 public static Order create(CustomerId customerId, ListOrderItem items) { Objects.requireNonNull(customerId, CustomerId cannot be null); Objects.requireNonNull(items, Items cannot be null); if (items.isEmpty()) { throw new IllegalArgumentException(Order must have at least one item); } return new Order(OrderId.generate(), customerId, items); } // 唯一通过聚合根修改状态 public void confirm() { if (this.status ! OrderStatus.PENDING) { throw new OrderStateException(Order can only be confirmed from PENDING state); } this.status OrderStatus.CONFIRMED; registerEvent(new OrderConfirmedEvent(this.id)); } public void pay(PaymentInfo paymentInfo) { if (this.status ! OrderStatus.CONFIRMED) { throw new OrderStateException(Order must be confirmed before payment); } this.paymentInfo paymentInfo; this.status OrderStatus.PAID; registerEvent(new OrderPaidEvent(this.id, paymentInfo.getTransactionId())); } public void ship(String trackingNumber) { if (this.status ! OrderStatus.PAID) { throw new OrderStateException(Order must be paid before shipping); } this.status OrderStatus.SHIPPED; registerEvent(new OrderShippedEvent(this.id, trackingNumber)); } }2.2 实体/** * 订单项实体 */ public class OrderItem { private ProductId productId; private String productName; private Money unitPrice; private int quantity; private Money subtotal; // 实体需要唯一标识 private OrderItemId id; public OrderItem(ProductId productId, String productName, Money unitPrice, int quantity) { this.id OrderItemId.generate(); this.productId productId; this.productName productName; this.unitPrice unitPrice; this.quantity quantity; this.subtotal unitPrice.multiply(quantity); } // 实体相等性基于ID Override public boolean equals(Object o) { if (this o) return true; if (o null || getClass() ! o.getClass()) return false; OrderItem that (OrderItem) o; return id.equals(that.id); } Override public int hashCode() { return id.hashCode(); } }2.3 值对象/** * 金额值对象 - 不可变表示货币金额 */ public final class Money { private final BigDecimal amount; private final Currency currency; public Money(BigDecimal amount, Currency currency) { this.amount amount.setScale(2, RoundingMode.HALF_UP); this.currency currency; } public static Money of(BigDecimal amount) { return new Money(amount, Currency.getInstance(CNY)); } public static Money of(double amount) { return new Money(BigDecimal.valueOf(amount), Currency.getInstance(CNY)); } // 值对象操作返回新实例 public Money add(Money other) { if (!this.currency.equals(other.currency)) { throw new IllegalArgumentException(Cannot add different currencies); } return new Money(this.amount.add(other.amount), this.currency); } public Money multiply(int factor) { return new Money(this.amount.multiply(BigDecimal.valueOf(factor)), this.currency); } // 值对象相等性基于属性值 Override public boolean equals(Object o) { if (this o) return true; if (o null || getClass() ! o.getClass()) return false; Money money (Money) o; return amount.compareTo(money.amount) 0 currency.equals(money.currency); } Override public int hashCode() { return Objects.hash(amount, currency); } } /** * 地址值对象 */ public final class ShippingAddress { private final String province; private final String city; private final String district; private final String street; private final String zipCode; public ShippingAddress(String province, String city, String district, String street, String zipCode) { this.province province; this.city city; this.district district; this.street street; this.zipCode zipCode; } public String getFullAddress() { return String.format(%s%s%s%s, province, city, district, street); } }三、仓储模式3.1 仓储接口/** * 仓储接口 - 属于领域层 */ public interface OrderRepository { // 根据ID查找 OptionalOrder findById(OrderId id); // 保存聚合 void save(Order order); // 批量保存 void saveAll(ListOrder orders); // 分页查询 PageOrder findByCustomerId(CustomerId customerId, PageRequest pageRequest); // 条件查询 ListOrder findByStatus(OrderStatus status); // 统计查询 long countByStatus(OrderStatus status); // 删除 void delete(OrderId id); }3.2 仓储实现/** * JPA仓储实现 - 属于基础设施层 */ Repository public class JpaOrderRepository implements OrderRepository { Autowired private OrderJpaRepository jpaRepository; Autowired private OrderMapper orderMapper; Override public OptionalOrder findById(OrderId id) { return jpaRepository.findById(id.getValue()) .map(orderMapper::toDomain); } Override public void save(Order order) { OrderEntity entity orderMapper.toEntity(order); jpaRepository.save(entity); } Override public PageOrder findByCustomerId(CustomerId customerId, PageRequest pageRequest) { return jpaRepository.findByCustomerId(customerId.getValue(), pageRequest) .map(orderMapper::toDomain); } } /** * MyBatis仓储实现 */ Mapper public interface OrderMyBatisRepository extends OrderRepository { Select(SELECT * FROM orders WHERE id #{id}) Results({ Result(property id, column id, id true), Result(property customerId, column customer_id), Result(property totalAmount, column total_amount), Result(property status, column status) }) OptionalOrder findById(Param(id) String id); }3.3 领域事件发布public interface DomainEventPublisher { void publish(DomainEvent event); void publishAll(ListDomainEvent events); } /** * Spring事件发布实现 */ Component public class SpringDomainEventPublisher implements DomainEventPublisher { Autowired private ApplicationEventPublisher eventPublisher; Override public void publish(DomainEvent event) { eventPublisher.publishEvent(new DomainEventWrapper(event)); } Override public void publishAll(ListDomainEvent events) { events.forEach(this::publish); } }四、领域服务4.1 领域服务定义/** * 领域服务 - 处理跨聚合的业务逻辑 */ DomainService public class OrderDomainService { /** * 计算订单总价包含优惠活动 */ public Money calculateOrderTotal(ListOrderItem items, ListPromotion applicablePromotions) { Money subtotal items.stream() .map(OrderItem::getSubtotal) .reduce(Money.ZERO, Money::add); Money discount applicablePromotions.stream() .map(promo - promo.calculateDiscount(subtotal)) .reduce(Money.ZERO, Money::add); return subtotal.subtract(discount); } /** * 验证订单是否可以取消 */ public boolean canCancel(Order order) { return order.getStatus() OrderStatus.PENDING || order.getStatus() OrderStatus.CONFIRMED; } /** * 执行订单取消 */ public Order cancel(Order order, CancelReason reason) { if (!canCancel(order)) { throw new IllegalStateException(Order cannot be cancelled in current state); } order.cancel(reason); return order; } }4.2 防腐层/** * 防腐层 - 隔离外部服务的影响 */ Component public class PaymentAdapter implements PaymentPort { Autowired private PaymentExternalService paymentService; Autowired private PaymentMapper mapper; Override public PaymentResult processPayment(PaymentRequest request) { try { ExternalPaymentRequest externalRequest mapper.toExternal(request); ExternalPaymentResponse response paymentService.pay(externalRequest); return mapper.toDomain(response); } catch (ExternalServiceException e) { throw new PaymentException(Payment processing failed, e); } } Override public RefundResult processRefund(RefundRequest request) { try { ExternalRefundRequest externalRequest mapper.toExternal(request); ExternalRefundResponse response paymentService.refund(externalRequest); return mapper.toDomain(response); } catch (ExternalServiceException e) { throw new RefundException(Refund processing failed, e); } } }五、应用服务5.1 应用服务设计/** * 应用服务 - 协调领域对象和外部服务 */ Service Transactional public class OrderApplicationService { Autowired private OrderRepository orderRepository; Autowired private ProductRepository productRepository; Autowired private PromotionService promotionService; Autowired private PaymentPort paymentPort; Autowired private DomainEventPublisher eventPublisher; /** * 创建订单 */ public OrderDTO createOrder(CreateOrderCommand command) { // 1. 获取商品信息 ListProduct products productRepository.findByIds(command.getProductIds()); // 2. 构建订单项 ListOrderItem items buildOrderItems(products, command.getQuantities()); // 3. 计算价格 ListPromotion promotions promotionService.getApplicablePromotions(command.getCustomerId()); Order order Order.create(command.getCustomerId(), items); Money finalAmount orderDomainService.calculateOrderTotal(items, promotions); // 4. 保存订单 orderRepository.save(order); // 5. 发布领域事件 eventPublisher.publishAll(order.getDomainEvents()); return orderMapper.toDTO(order); } /** * 支付订单 */ public PaymentDTO payOrder(PayOrderCommand command) { Order order orderRepository.findById(command.getOrderId()) .orElseThrow(() - new OrderNotFoundException(command.getOrderId())); PaymentRequest request PaymentRequest.builder() .orderId(order.getId()) .amount(order.getTotalAmount()) .paymentMethod(command.getPaymentMethod()) .build(); PaymentResult result paymentPort.processPayment(request); if (result.isSuccess()) { order.pay(result.getTransactionId()); orderRepository.save(order); } return paymentMapper.toDTO(result); } }5.2 命令与查询分离/** * 命令端点 */ RestController RequestMapping(/api/orders) RequiredArgsConstructor public class OrderCommandController { private final OrderApplicationService orderService; PostMapping public ResponseEntityOrderDTO createOrder(RequestBody Valid CreateOrderCommand command) { OrderDTO result orderService.createOrder(command); return ResponseEntity.status(HttpStatus.CREATED).body(result); } PostMapping(/{id}/pay) public ResponseEntityPaymentDTO payOrder( PathVariable String id, RequestBody Valid PayOrderCommand command) { command.setOrderId(OrderId.of(id)); PaymentDTO result orderService.payOrder(command); return ResponseEntity.ok(result); } } /** * 查询端点 */ RestController RequestMapping(/api/orders) RequiredArgsConstructor public class OrderQueryController { private final OrderQueryService queryService; GetMapping(/{id}) public ResponseEntityOrderDTO getOrder(PathVariable String id) { return queryService.findById(id) .map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()); } GetMapping public ResponseEntityPageOrderDTO listOrders( RequestParam String customerId, RequestParam(defaultValue 0) int page, RequestParam(defaultValue 20) int size) { PageOrderDTO result queryService.findByCustomerId(customerId, PageRequest.of(page, size)); return ResponseEntity.ok(result); } }六、限界上下文集成6.1 上下文映射/** * 客户上下文的客户DTO */ public class CustomerDTO { private CustomerId id; private String name; private Email email; } /** * 订单上下文使用客户信息 */ public class Order { private CustomerId customerId; // 不直接持有Customer对象而是引用其ID } /** * 上下文间通信 - 使用防腐层 */ Service public class CustomerIntegrationService { Autowired private CustomerService customerService; // 外部客户服务 public Customer getCustomer(CustomerId customerId) { CustomerDTO dto customerService.getById(customerId.getValue()); return CustomerMapper.toDomain(dto); } public boolean isVipCustomer(CustomerId customerId) { CustomerDTO dto customerService.getById(customerId.getValue()); return dto.isVip(); } }6.2 消息集成/** * 发布客户注册事件 */ RabbitListener(queues customer.events) public class CustomerEventListener { Autowired private OrderRepository orderRepository; Autowired private LoyaltyPointService loyaltyPointService; RabbitHandler public void handleCustomerRegistered(CustomerRegisteredEvent event) { // 为新注册客户创建初始订单记录或积分 loyaltyPointService.createAccount(event.getCustomerId()); } } /** * 发布订单事件供其他上下文消费 */ Service public class OrderEventPublisher { Autowired private RabbitTemplate rabbitTemplate; public void publishOrderCreated(OrderCreatedEvent event) { OrderCreatedMessage message OrderCreatedMessage.builder() .orderId(event.getOrderId().getValue()) .customerId(event.getCustomerId().getValue()) .totalAmount(event.getTotalAmount().getValue()) .build(); rabbitTemplate.convertAndSend(order.events, order.created, message); } }七、DDD与微服务7.1 按DDD划分微服务┌─────────────────────────────────────────────────────────────────┐ │ 订单限界上下文 │ │ 订单聚合 | 订单项值对象 | 订单领域服务 │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 客户限界上下文 │ │ 客户聚合 | 地址值对象 | 客户领域服务 │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 产品限界上下文 │ │ 产品聚合 | 库存实体 | 价格值对象 │ └─────────────────────────────────────────────────────────────────┘7.2 微服务间通信/** * REST同步调用 */ Service public class ProductIntegration { Autowired private WebClient webClient; public ListProduct getProducts(ListProductId ids) { return webClient.post() .uri(http://product-service/api/products/batch) .bodyValue(ids.stream().map(ProductId::getValue).collect(Collectors.toList())) .retrieve() .bodyToFlux(ProductDTO.class) .map(this::toDomain) .collect(Collectors.toList()) .block(); } } /** * 消息异步通信 */ Service public class OrderNotificationService { Autowired private RabbitTemplate rabbitTemplate; public void notifyOrderCreated(Order order) { OrderNotification notification OrderNotification.builder() .orderId(order.getId().getValue()) .customerEmail(order.getCustomerId().getValue()) // 需要查询客户服务 .items(order.getItems().size()) .build(); rabbitTemplate.convertAndSend(notifications.order.created, notification); } }八、实践建议8.1 DDD实施清单阶段任务产出物战略设计识别限界上下文上下文映射图定义通用语言领域词汇表确定核心域优先级矩阵战术设计设计聚合聚合图定义实体和值对象领域模型设计仓储接口仓储接口定义实现实现领域层聚合、领域服务实现应用层应用服务实现基础设施仓储实现、集成8.2 常见问题处理问题解决方案聚合过大拆分为多个小聚合贫血模型将行为移入领域对象循环依赖使用领域事件解耦仓储滥用领域服务只依赖接口九、总结领域驱动设计是构建复杂业务系统的重要方法论通过本文的介绍你可以DDD核心概念限界上下文、聚合、实体、值对象聚合设计聚合根、实体、值对象的实现仓储模式仓储接口定义和实现领域服务处理跨聚合的业务逻辑应用服务协调领域对象和外部服务限界上下文集成上下文映射和通信模式DDD与微服务按DDD划分微服务边界DDD不仅是一种技术方案更是一种思维方式需要业务专家和开发团队的紧密协作才能真正发挥其价值。