B.10.01.6-DDD领域驱动设计:从理论到落地的完整指南
title: “DDD领域驱动设计:从理论到落地的完整指南”
subtitle: “构建复杂业务系统的架构思维与实践方法”
date: “2024-12-09”
category: “代码内功修炼”
tags: [“DDD”, “领域驱动设计”, “架构设计”, “业务建模”]
description: “深入解析DDD核心概念,提供从业务分析到代码实现的完整实践指南”
🎯 业务场景:电商平台的复杂业务挑战
背景挑战
假如你正在负责一个大型电商平台的架构设计,面临以下典型问题:
业务复杂性爆炸:
- 订单管理:普通订单、预售订单、团购订单、秒杀订单等多种类型
- 库存管理:实物库存、虚拟库存、预占库存、安全库存等复杂逻辑
- 价格计算:会员价、促销价、阶梯价、区域价等多维度定价
- 支付流程:支付、退款、分期、代付等多种支付场景
技术债务累积:
- 代码耦合严重:订单、库存、支付逻辑混杂在一起
- 数据模型混乱:同一个概念在不同模块有不同的表示
- 业务规则分散:相同的业务逻辑在多处重复实现
- 扩展困难:新增业务类型需要修改大量现有代码
量化指标:
- 支持 20+ 种不同的订单类型
- 管理 100万+ SKU商品库存
- 处理 日均50万 笔订单
🔍 技术分析:DDD核心概念与传统开发对比
DDD vs 传统开发模式
对比维度 | 传统开发模式 | DDD领域驱动设计 |
---|---|---|
设计起点 | 数据库表结构 | 业务领域模型 |
代码组织 | 按技术分层(Controller、Service、DAO) | 按业务领域分模块 |
业务逻辑 | 分散在Service层 | 封装在领域对象中 |
数据模型 | 贫血模型(只有getter/setter) | 充血模型(包含业务行为) |
团队协作 | 技术人员主导 | 业务专家与技术人员协作 |
变更响应 | 牵一发动全身 | 领域内变更影响范围可控 |
DDD分层架构
核心概念解析
1. 限界上下文 (Bounded Context)
定义:明确的业务边界,在这个边界内,特定的领域模型是一致和有效的。
电商平台的限界上下文划分:
- 商品目录上下文:商品信息管理、分类、搜索
- 订单管理上下文:订单创建、状态流转、订单履行
- 库存管理上下文:库存扣减、补货、盘点
- 支付上下文:支付处理、退款、对账
- 用户上下文:用户注册、认证、权限管理
2. 聚合 (Aggregate)
定义:一组相关对象的集合,作为数据修改的单元,确保业务不变性。
设计原则:
- 一个聚合只有一个聚合根
- 聚合内部保证强一致性
- 聚合之间通过ID引用,保证最终一致性
- 聚合应该尽可能小
3. 领域事件 (Domain Event)
定义:领域中发生的重要业务事件,用于解耦不同聚合之间的交互。
🏗️ 架构设计:订单聚合的DDD实现
1. 订单聚合设计
聚合根:Order
@Entity
@Table(name = "orders")
public class Order {@EmbeddedIdprivate OrderId orderId;@Embeddedprivate CustomerId customerId;@ElementCollection@CollectionTable(name = "order_items")private List<OrderItem> orderItems;@Embeddedprivate ShippingAddress shippingAddress;@Enumerated(EnumType.STRING)private OrderStatus status;@Embeddedprivate Money totalAmount;private LocalDateTime createdAt;private LocalDateTime updatedAt;// 领域事件@Transientprivate List<DomainEvent> domainEvents = new ArrayList<>();// 私有构造函数,强制通过工厂方法创建protected Order() {}// 工厂方法:创建订单public static Order create(CustomerId customerId, List<OrderItem> items, ShippingAddress shippingAddress) {// 业务规则验证validateOrderCreation(customerId, items, shippingAddress);Order order = new Order();order.orderId = OrderId.generate();order.customerId = customerId;order.orderItems = new ArrayList<>(items);order.shippingAddress = shippingAddress;order.status = OrderStatus.PENDING;order.totalAmount = calculateTotalAmount(items);order.createdAt = LocalDateTime.now();order.updatedAt = LocalDateTime.now();// 发布领域事件order.addDomainEvent(new OrderCreatedEvent(order.orderId, order.customerId));return order;}// 业务行为:确认订单public void confirm() {// 业务规则:只有待处理的订单才能确认if (this.status != OrderStatus.PENDING) {throw new IllegalOrderStateException(String.format("订单状态为%s,无法确认", this.status));}this.status = OrderStatus.CONFIRMED;this.updatedAt = LocalDateTime.now();// 发布领域事件addDomainEvent(new OrderConfirmedEvent(this.orderId, this.customerId));}// 业务行为:取消订单public void cancel(String reason) {// 业务规则:已发货的订单不能取消if (this.status == OrderStatus.SHIPPED || this.status == OrderStatus.DELIVERED) {throw new IllegalOrderStateException("订单已发货,无法取消");}if (this.status == OrderStatus.CANCELLED) {return; // 幂等性处理}this.status = OrderStatus.CANCELLED;this.updatedAt = LocalDateTime.now();// 发布领域事件addDomainEvent(new OrderCancelledEvent(this.orderId, this.customerId, reason));}// 业务行为:添加订单项public void addOrderItem(OrderItem item) {// 业务规则:只有待处理的订单才能添加商品if (this.status != OrderStatus.PENDING) {throw new IllegalOrderStateException("订单已确认,无法添加商品");}// 检查是否已存在相同商品Optional<OrderItem> existingItem = orderItems.stream().filter(oi -> oi.getProductId().equals(item.getProductId())).findFirst();if (existingItem.isPresent()) {// 合并数量existingItem.get().increaseQuantity(item.getQuantity());} else {// 添加新商品this.orderItems.add(item);}// 重新计算总金额this.totalAmount = calculateTotalAmount(this.orderItems);this.updatedAt = LocalDateTime.now();}// 业务查询:是否可以取消public boolean canBeCancelled() {return this.status == OrderStatus.PENDING || this.status == OrderStatus.CONFIRMED;}// 业务查询:获取订单摘要public OrderSummary getSummary() {return OrderSummary.builder().orderId(this.orderId.getValue()).customerName(getCustomerName()) // 通过领域服务获取.itemCount(this.orderItems.size()).totalAmount(this.totalAmount.getAmount()).status(this.status.getDisplayName()).createdAt(this.createdAt).build();}// 私有方法:验证订单创建private static void validateOrderCreation(CustomerId customerId, List<OrderItem> items, ShippingAddress shippingAddress) {if (customerId == null) {throw new IllegalArgumentException("客户ID不能为空");}if (items == null || items.isEmpty()) {throw new IllegalArgumentException("订单商品不能为空");}if (shippingAddress == null) {throw new IllegalArgumentException("收货地址不能为空");}// 验证商品数量for (OrderItem item : items) {if (item.getQuantity() <= 0) {throw new IllegalArgumentException("商品数量必须大于0");}}}// 私有方法:计算总金额private static Money calculateTotalAmount(List<OrderItem> items) {BigDecimal total = items.stream().map(item -> item.getSubtotal().getAmount()).reduce(BigDecimal.ZERO, BigDecimal::add);return new Money(total);}// 领域事件管理public void addDomainEvent(DomainEvent event) {this.domainEvents.add(event);}public List<DomainEvent> getDomainEvents() {return Collections.unmodifiableList(domainEvents);}public void clearDomainEvents() {this.domainEvents.clear();}// Getterspublic OrderId getOrderId() { return orderId; }public CustomerId getCustomerId() { return customerId; }public List<OrderItem> getOrderItems() { return Collections.unmodifiableList(orderItems); }public OrderStatus getStatus() { return status; }public Money getTotalAmount() { return totalAmount; }public LocalDateTime getCreatedAt() { return createdAt; }
}
值对象:OrderId
@Embeddable
public class OrderId {@Column(name = "order_id")private String value;// JPA需要的默认构造函数protected OrderId() {}private OrderId(String value) {if (StringUtils.isBlank(value)) {throw new IllegalArgumentException("订单ID不能为空");}this.value = value;}public static OrderId of(String value) {return new OrderId(value);}public static OrderId generate() {String id = "ORD" + System.currentTimeMillis() + String.format("%04d", new Random().nextInt(10000));return new OrderId(id);}public String getValue() {return value;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;OrderId orderId = (OrderId) o;return Objects.equals(value, orderId.value);}@Overridepublic int hashCode() {return Objects.hash(value);}@Overridepublic String toString() {return value;}
}
值对象:Money
@Embeddable
public class Money {@Column(name = "amount", precision = 19, scale = 2)private BigDecimal amount;@Column(name = "currency")private String currency;protected Money() {}public Money(BigDecimal amount) {this(amount, "CNY");}public Money(BigDecimal amount, String currency) {if (amount == null) {throw new IllegalArgumentException("金额不能为空");}if (amount.compareTo(BigDecimal.ZERO) < 0) {throw new IllegalArgumentException("金额不能为负数");}if (StringUtils.isBlank(currency)) {throw new IllegalArgumentException("货币类型不能为空");}this.amount = amount.setScale(2, RoundingMode.HALF_UP);this.currency = currency;}public Money add(Money other) {if (!this.currency.equals(other.currency)) {throw new IllegalArgumentException("不同货币类型无法相加");}return new Money(this.amount.add(other.amount), this.currency);}public Money subtract(Money other) {if (!this.currency.equals(other.currency)) {throw new IllegalArgumentException("不同货币类型无法相减");}BigDecimal result = this.amount.subtract(other.amount);if (result.compareTo(BigDecimal.ZERO) < 0) {throw new IllegalArgumentException("计算结果不能为负数");}return new Money(result, this.currency);}public Money multiply(int multiplier) {return new Money(this.amount.multiply(BigDecimal.valueOf(multiplier)), this.currency);}public boolean isGreaterThan(Money other) {if (!this.currency.equals(other.currency)) {throw new IllegalArgumentException("不同货币类型无法比较");}return this.amount.compareTo(other.amount) > 0;}public boolean isZero() {return this.amount.compareTo(BigDecimal.ZERO) == 0;}// Getterspublic BigDecimal getAmount() { return amount; }public String getCurrency() { return currency; }@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Money money = (Money) o;return Objects.equals(amount, money.amount) && Objects.equals(currency, money.currency);}@Overridepublic int hashCode() {return Objects.hash(amount, currency);}@Overridepublic String toString() {return String.format("%s %s", amount, currency);}
}
2. 领域服务实现
订单定价服务
@Component
public class OrderPricingService {private final PricingRuleRepository pricingRuleRepository;private final CustomerRepository customerRepository;public OrderPricingService(PricingRuleRepository pricingRuleRepository,CustomerRepository customerRepository) {this.pricingRuleRepository = pricingRuleRepository;this.customerRepository = customerRepository;}/*** 计算订单价格*/public OrderPricing calculatePricing(Order order) {Customer customer = customerRepository.findById(order.getCustomerId()).orElseThrow(() -> new CustomerNotFoundException(order.getCustomerId()));// 获取适用的定价规则List<PricingRule> applicableRules = pricingRuleRepository.findApplicableRules(customer.getMemberLevel(), order.getTotalAmount());Money originalAmount = order.getTotalAmount();Money discountAmount = Money.ZERO;List<AppliedDiscount> appliedDiscounts = new ArrayList<>();// 应用定价规则for (PricingRule rule : applicableRules) {if (rule.isApplicable(order, customer)) {Money ruleDiscount = rule.calculateDiscount(order, customer);discountAmount = discountAmount.add(ruleDiscount);appliedDiscounts.add(new AppliedDiscount(rule.getRuleName(),rule.getDescription(),ruleDiscount));}}Money finalAmount = originalAmount.subtract(discountAmount);return OrderPricing.builder().originalAmount(originalAmount).discountAmount(discountAmount).finalAmount(finalAmount).appliedDiscounts(appliedDiscounts).build();}
}
3. 应用服务实现
订单应用服务
@Service
@Transactional
public class OrderApplicationService {private final OrderRepository orderRepository;private final CustomerRepository customerRepository;private final ProductRepository productRepository;private final OrderPricingService orderPricingService;private final DomainEventPublisher eventPublisher;public OrderApplicationService(OrderRepository orderRepository,CustomerRepository customerRepository,ProductRepository productRepository,OrderPricingService orderPricingService,DomainEventPublisher eventPublisher) {this.orderRepository = orderRepository;this.customerRepository = customerRepository;this.productRepository = productRepository;this.orderPricingService = orderPricingService;this.eventPublisher = eventPublisher;}/*** 创建订单*/public CreateOrderResult createOrder(CreateOrderCommand command) {// 1. 验证客户存在Customer customer = customerRepository.findById(command.getCustomerId()).orElseThrow(() -> new CustomerNotFoundException(command.getCustomerId()));// 2. 验证商品存在并构建订单项List<OrderItem> orderItems = buildOrderItems(command.getOrderItems());// 3. 创建订单聚合Order order = Order.create(command.getCustomerId(),orderItems,command.getShippingAddress());// 4. 计算价格OrderPricing pricing = orderPricingService.calculatePricing(order);order.applyPricing(pricing);// 5. 保存订单orderRepository.save(order);// 6. 发布领域事件publishDomainEvents(order);return CreateOrderResult.builder().orderId(order.getOrderId().getValue()).totalAmount(order.getTotalAmount().getAmount()).status(order.getStatus().name()).build();}/*** 确认订单*/public void confirmOrder(ConfirmOrderCommand command) {Order order = orderRepository.findById(command.getOrderId()).orElseThrow(() -> new OrderNotFoundException(command.getOrderId()));// 执行业务逻辑order.confirm();// 保存变更orderRepository.save(order);// 发布领域事件publishDomainEvents(order);}/*** 取消订单*/public void cancelOrder(CancelOrderCommand command) {Order order = orderRepository.findById(command.getOrderId()).orElseThrow(() -> new OrderNotFoundException(command.getOrderId()));// 执行业务逻辑order.cancel(command.getReason());// 保存变更orderRepository.save(order);// 发布领域事件publishDomainEvents(order);}/*** 查询订单详情*/@Transactional(readOnly = true)public OrderDetailResult getOrderDetail(String orderId) {Order order = orderRepository.findById(OrderId.of(orderId)).orElseThrow(() -> new OrderNotFoundException(OrderId.of(orderId)));return OrderDetailResult.from(order);}// 私有方法:构建订单项private List<OrderItem> buildOrderItems(List<CreateOrderItemCommand> itemCommands) {List<OrderItem> orderItems = new ArrayList<>();for (CreateOrderItemCommand itemCommand : itemCommands) {Product product = productRepository.findById(itemCommand.getProductId()).orElseThrow(() -> new ProductNotFoundException(itemCommand.getProductId()));// 检查库存if (!product.hasEnoughStock(itemCommand.getQuantity())) {throw new InsufficientStockException(itemCommand.getProductId());}OrderItem orderItem = OrderItem.create(itemCommand.getProductId(),product.getName(),product.getPrice(),itemCommand.getQuantity());orderItems.add(orderItem);}return orderItems;}// 私有方法:发布领域事件private void publishDomainEvents(Order order) {List<DomainEvent> events = order.getDomainEvents();for (DomainEvent event : events) {eventPublisher.publish(event);}order.clearDomainEvents();}
}
🛠️ 代码实现:完整的DDD项目结构
项目结构组织
src/main/java/com/ecommerce/
├── order/ # 订单限界上下文
│ ├── domain/ # 领域层
│ │ ├── model/ # 领域模型
│ │ │ ├── Order.java # 聚合根
│ │ │ ├── OrderItem.java # 实体
│ │ │ ├── OrderId.java # 值对象
│ │ │ ├── Money.java # 值对象
│ │ │ └── OrderStatus.java # 枚举
│ │ ├── service/ # 领域服务
│ │ │ └── OrderPricingService.java
│ │ ├── repository/ # 仓储接口
│ │ │ └── OrderRepository.java
│ │ └── event/ # 领域事件
│ │ ├── OrderCreatedEvent.java
│ │ ├── OrderConfirmedEvent.java
│ │ └── OrderCancelledEvent.java
│ ├── application/ # 应用层
│ │ ├── service/ # 应用服务
│ │ │ └── OrderApplicationService.java
│ │ ├── command/ # 命令对象
│ │ │ ├── CreateOrderCommand.java
│ │ │ ├── ConfirmOrderCommand.java
│ │ │ └── CancelOrderCommand.java
│ │ └── result/ # 结果对象
│ │ ├── CreateOrderResult.java
│ │ └── OrderDetailResult.java
│ ├── infrastructure/ # 基础设施层
│ │ ├── repository/ # 仓储实现
│ │ │ └── JpaOrderRepository.java
│ │ └── event/ # 事件发布实现
│ │ └── SpringDomainEventPublisher.java
│ └── interfaces/ # 用户接口层
│ ├── rest/ # REST API
│ │ └── OrderController.java
│ └── dto/ # 数据传输对象
│ ├── CreateOrderRequest.java
│ └── OrderResponse.java
├── inventory/ # 库存限界上下文
├── payment/ # 支付限界上下文
└── shared/ # 共享内核├── domain/│ ├── DomainEvent.java│ └── DomainEventPublisher.java└── infrastructure/└── config/
仓储模式实现
仓储接口
public interface OrderRepository {/*** 保存订单*/void save(Order order);/*** 根据ID查找订单*/Optional<Order> findById(OrderId orderId);/*** 根据客户ID查找订单列表*/List<Order> findByCustomerId(CustomerId customerId);/*** 根据状态查找订单*/List<Order> findByStatus(OrderStatus status);/*** 分页查询订单*/Page<Order> findAll(Pageable pageable);/*** 删除订单*/void delete(Order order);/*** 检查订单是否存在*/boolean existsById(OrderId orderId);
}
JPA仓储实现
@Repository
public class JpaOrderRepository implements OrderRepository {private final SpringDataOrderRepository springDataRepository;public JpaOrderRepository(SpringDataOrderRepository springDataRepository) {this.springDataRepository = springDataRepository;}@Overridepublic void save(Order order) {springDataRepository.save(order);}@Overridepublic Optional<Order> findById(OrderId orderId) {return springDataRepository.findById(orderId.getValue());}@Overridepublic List<Order> findByCustomerId(CustomerId customerId) {return springDataRepository.findByCustomerId(customerId.getValue());}@Overridepublic List<Order> findByStatus(OrderStatus status) {return springDataRepository.findByStatus(status);}@Overridepublic Page<Order> findAll(Pageable pageable) {return springDataRepository.findAll(pageable);}@Overridepublic void delete(Order order) {springDataRepository.delete(order);}@Overridepublic boolean existsById(OrderId orderId) {return springDataRepository.existsById(orderId.getValue());}
}// Spring Data JPA接口
interface SpringDataOrderRepository extends JpaRepository<Order, String> {List<Order> findByCustomerId(String customerId);List<Order> findByStatus(OrderStatus status);@Query("SELECT o FROM Order o WHERE o.createdAt BETWEEN :startDate AND :endDate")List<Order> findByCreatedAtBetween(@Param("startDate") LocalDateTime startDate,@Param("endDate") LocalDateTime endDate);
}
领域事件处理
事件发布器
@Component
public class SpringDomainEventPublisher implements DomainEventPublisher {private final ApplicationEventPublisher applicationEventPublisher;public SpringDomainEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher;}@Overridepublic void publish(DomainEvent event) {applicationEventPublisher.publishEvent(event);}
}
事件处理器
@Component
@Slf4j
public class OrderEventHandler {private final InventoryService inventoryService;private final NotificationService notificationService;private final EmailService emailService;public OrderEventHandler(InventoryService inventoryService,NotificationService notificationService,EmailService emailService) {this.inventoryService = inventoryService;this.notificationService = notificationService;this.emailService = emailService;}/*** 处理订单创建事件*/@EventListener@Asyncpublic void handleOrderCreated(OrderCreatedEvent event) {log.info("处理订单创建事件:{}", event.getOrderId());try {// 预占库存inventoryService.reserveInventory(event.getOrderId(), event.getOrderItems());// 发送订单确认邮件emailService.sendOrderConfirmationEmail(event.getCustomerId(), event.getOrderId());log.info("订单创建事件处理完成:{}", event.getOrderId());} catch (Exception e) {log.error("处理订单创建事件失败:{}", event.getOrderId(), e);// 这里可以实现补偿机制或重试逻辑}}/*** 处理订单确认事件*/@EventListener@Asyncpublic void handleOrderConfirmed(OrderConfirmedEvent event) {log.info("处理订单确认事件:{}", event.getOrderId());try {// 扣减库存inventoryService.deductInventory(event.getOrderId());// 发送确认通知notificationService.sendOrderConfirmedNotification(event.getCustomerId(), event.getOrderId());log.info("订单确认事件处理完成:{}", event.getOrderId());} catch (Exception e) {log.error("处理订单确认事件失败:{}", event.getOrderId(), e);}}/*** 处理订单取消事件*/@EventListener@Asyncpublic void handleOrderCancelled(OrderCancelledEvent event) {log.info("处理订单取消事件:{}", event.getOrderId());try {// 释放库存inventoryService.releaseInventory(event.getOrderId());// 发送取消通知notificationService.sendOrderCancelledNotification(event.getCustomerId(), event.getOrderId(), event.getReason());log.info("订单取消事件处理完成:{}", event.getOrderId());} catch (Exception e) {log.error("处理订单取消事件失败:{}", event.getOrderId(), e);}}
}
REST API实现
订单控制器
@RestController
@RequestMapping("/api/orders")
@Validated
public class OrderController {private final OrderApplicationService orderApplicationService;public OrderController(OrderApplicationService orderApplicationService) {this.orderApplicationService = orderApplicationService;}/*** 创建订单*/@PostMappingpublic ResponseEntity<ApiResponse<CreateOrderResponse>> createOrder(@Valid @RequestBody CreateOrderRequest request) {CreateOrderCommand command = CreateOrderCommand.builder().customerId(CustomerId.of(request.getCustomerId())).orderItems(convertToOrderItems(request.getItems())).shippingAddress(convertToShippingAddress(request.getShippingAddress())).build();CreateOrderResult result = orderApplicationService.createOrder(command);CreateOrderResponse response = CreateOrderResponse.builder().orderId(result.getOrderId()).totalAmount(result.getTotalAmount()).status(result.getStatus()).build();return ResponseEntity.ok(ApiResponse.success(response));}/*** 确认订单*/@PostMapping("/{orderId}/confirm")public ResponseEntity<ApiResponse<Void>> confirmOrder(@PathVariable String orderId) {ConfirmOrderCommand command = ConfirmOrderCommand.builder().orderId(OrderId.of(orderId)).build();orderApplicationService.confirmOrder(command);return ResponseEntity.ok(ApiResponse.success());}/*** 取消订单*/@PostMapping("/{orderId}/cancel")public ResponseEntity<ApiResponse<Void>> cancelOrder(@PathVariable String orderId,@Valid @RequestBody CancelOrderRequest request) {CancelOrderCommand command = CancelOrderCommand.builder().orderId(OrderId.of(orderId)).reason(request.getReason()).build();orderApplicationService.cancelOrder(command);return ResponseEntity.ok(ApiResponse.success());}/*** 查询订单详情*/@GetMapping("/{orderId}")public ResponseEntity<ApiResponse<OrderDetailResponse>> getOrderDetail(@PathVariable String orderId) {OrderDetailResult result = orderApplicationService.getOrderDetail(orderId);OrderDetailResponse response = OrderDetailResponse.from(result);return ResponseEntity.ok(ApiResponse.success(response));}// 私有转换方法private List<CreateOrderItemCommand> convertToOrderItems(List<CreateOrderItemRequest> items) {return items.stream().map(item -> CreateOrderItemCommand.builder().productId(ProductId.of(item.getProductId())).quantity(item.getQuantity()).build()).collect(Collectors.toList());}private ShippingAddress convertToShippingAddress(ShippingAddressRequest request) {return ShippingAddress.builder().receiverName(request.getReceiverName()).phone(request.getPhone()).province(request.getProvince()).city(request.getCity()).district(request.getDistrict()).detailAddress(request.getDetailAddress()).build();}
}
💡 经验总结:DDD落地最佳实践
业务复杂度管理效果
踩坑经验总结
坑1:过度建模
问题描述:为了追求完美的领域模型,过度抽象和建模
// ❌ 错误示例:过度抽象的订单状态
public abstract class OrderState {public abstract void confirm(Order order);public abstract void cancel(Order order);public abstract void ship(Order order);// ... 20多个状态转换方法
}public class PendingOrderState extends OrderState {// 实现所有方法,大部分抛异常
}// ✅ 正确做法:简单的枚举 + 业务规则
public enum OrderStatus {PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED;public boolean canTransitionTo(OrderStatus target) {return VALID_TRANSITIONS.get(this).contains(target);}
}
解决方案:
- 从简单开始,逐步演进
- 只对真正复杂的业务逻辑建模
- 保持模型的简洁性和实用性
坑2:聚合边界划分不当
问题描述:聚合设计过大或过小,影响性能和一致性
// ❌ 错误示例:聚合过大
public class Order {private List<OrderItem> items;private Customer customer; // 不应该包含完整客户信息private List<Payment> payments; // 支付应该是独立聚合private Inventory inventory; // 库存应该是独立聚合// ... 包含了太多不相关的概念
}// ✅ 正确做法:合理的聚合边界
public class Order {private OrderId orderId;private CustomerId customerId; // 只保存ID引用private List<OrderItem> items;private OrderStatus status;private Money totalAmount;// 只包含订单核心概念
}
解决方案:
- 聚合应该围绕业务不变性设计
- 聚合间通过ID引用,避免直接对象引用
- 一个事务只修改一个聚合
- 聚合大小要适中,通常不超过几个实体
坑3:忽略性能考虑
问题描述:过度追求领域模型纯粹性,忽略性能优化
// ❌ 错误示例:每次都重新计算
public class Order {public Money getTotalAmount() {return orderItems.stream().map(OrderItem::getSubtotal).reduce(Money.ZERO, Money::add); // 每次调用都重新计算}
}// ✅ 正确做法:缓存计算结果
public class Order {private Money totalAmount; // 缓存计算结果public void addOrderItem(OrderItem item) {this.orderItems.add(item);this.totalAmount = calculateTotalAmount(); // 状态变更时重新计算}public Money getTotalAmount() {return totalAmount; // 直接返回缓存值}
}
最佳实践指南
1. 领域建模流程
具体步骤:
- 业务调研:深入了解业务流程和规则
- 事件风暴:识别领域事件和业务流程
- 识别聚合:找出数据一致性边界
- 定义限界上下文:划分业务边界
- 设计领域模型:创建实体、值对象、聚合
- 编码实现:将模型转换为代码
- 测试验证:确保业务逻辑正确
- 持续重构:根据业务变化调整模型
2. 代码组织原则
按领域组织:
src/main/java/com/company/
├── order/ # 订单领域
├── inventory/ # 库存领域
├── payment/ # 支付领域
└── customer/ # 客户领域
分层清晰:
- 领域层:纯业务逻辑,不依赖外部
- 应用层:协调领域对象,处理用例
- 基础设施层:技术实现,数据持久化
- 接口层:对外暴露API
3. 测试策略
单元测试:
@Test
public void should_confirm_order_when_status_is_pending() {// GivenOrder order = Order.create(customerId, orderItems, shippingAddress);// Whenorder.confirm();// ThenassertThat(order.getStatus()).isEqualTo(OrderStatus.CONFIRMED);assertThat(order.getDomainEvents()).hasSize(1);assertThat(order.getDomainEvents().get(0)).isInstanceOf(OrderConfirmedEvent.class);
}
集成测试:
@SpringBootTest
@Transactional
public class OrderApplicationServiceTest {@Testpublic void should_create_order_successfully() {// GivenCreateOrderCommand command = buildCreateOrderCommand();// WhenCreateOrderResult result = orderApplicationService.createOrder(command);// ThenassertThat(result.getOrderId()).isNotNull();// 验证数据库状态Order savedOrder = orderRepository.findById(OrderId.of(result.getOrderId())).get();assertThat(savedOrder.getStatus()).isEqualTo(OrderStatus.PENDING);}
}
🎯 总结:DDD的价值与未来
DDD不仅仅是一种技术实践,更是一种思维方式的转变。
记住:DDD的核心价值在于让技术更好地服务于业务,让复杂的业务逻辑变得清晰可控。好的领域模型应该是业务专家和技术专家都能理解的共同语言。