企业级订单环境架构设计:领域驱动 vs 资料驱动实践指南

本文通过订单系统案例,对比分析领域驱动设计(DDD)与传统三层架构的差异,帮助你在实际项目中做出正确的架构选择。

1. 项目背景与架构决策

1.1 业务需求分析

我们需要构建一个订单处理系统,核心需求包括:

  • 多渠道订单创建和状态管理
  • 实时库存验证和预留
  • 动态价格计算
  • 订单全生命周期追踪
  • 与外部系统(库存、客户、定价)集成

1.2 架构选择:二选一的关键决策

在系统设计时,我们需要在两种架构模式中做出选择:

架构模式核心思想适用场景技术特点
数据驱动架构以数据模型为中心,贫血模型简单CRUD、报表系统、快速交付实体层+服务层,业务逻辑在Service
领域驱动架构以业务模型为中心,充血模型复杂业务规则、长期演进系统领域层为核心,业务逻辑在Domain

我们的选择:基于订单系统的业务复杂性,选择领域驱动架构

2. 系统架构设计

2.1 整体架构图

基础设施层
领域层 - 核心业务逻辑
应用层
表现层
数据库仓储
外部服务适配器
消息队列
订单聚合
客户实体
产品实体
领域服务
领域事件
订单应用服务
查询应用服务
Web前端
移动APP
API网关

3. 领域驱动设计实现

3.1 领域模型设计

3.1.1 领域类图
1
many
1
1
1
1
Order
-OrderId id
-CustomerId customerId
-OrderStatus status
-List<OrderItem> items
-Money totalAmount
+create(customerId, items) : Order
+addItem(product, quantity) : void
+removeItem(itemId) : void
+approve() : void
+cancel() : void
+calculateTotal() : Money
-validateState() : void
OrderItem
-OrderItemId id
-ProductId productId
-Quantity quantity
-Money unitPrice
+create(productId, quantity, unitPrice) : OrderItem
+updateQuantity(quantity) : void
+calculateSubtotal() : Money
-validateState() : void
Customer
-CustomerId id
-String name
-CustomerStatus status
+changeName(name) : void
+activate() : void
+deactivate() : void
+isActive() : boolean
Product
-ProductId id
-String name
-Money price
-StockQuantity stock
+changePrice(newPrice) : void
+reduceStock(quantity) : void
+increaseStock(quantity) : void
+isAvailable(quantity) : boolean
3.1.2 领域事件设计
«interface»
DomainEvent
+getEventId() : String
+getOccurredOn() : Timestamp
+getAggregateId() : String
OrderCreatedEvent
-OrderId orderId
-CustomerId customerId
-Money totalAmount
+getOrderId() : OrderId
+getCustomerId() : CustomerId
+getTotalAmount() : Money
OrderApprovedEvent
-OrderId orderId
-Timestamp approvedAt
InventoryReservedEvent
-OrderId orderId
-List<ProductId> reservedProducts

3.2 核心领域实现

/**
* 订单聚合根 - 领域驱动设计核心
* 职责:封装订单相关业务逻辑和状态管理
*/
public class Order {
private final OrderId id;
private final CustomerId customerId;
private OrderStatus status;
private final List<OrderItem> items;
  private Money totalAmount;
  private final Timestamp createdAt;
  private Timestamp updatedAt;
  // 私有构造函数,强制使用工厂方法
  private Order(OrderId id, CustomerId customerId, List<OrderItem> items) {
    this.id = Objects.requireNonNull(id, "订单ID不能为空");
    this.customerId = Objects.requireNonNull(customerId, "客户ID不能为空");
    this.items = new ArrayList<>(Objects.requireNonNull(items, "订单项不能为空"));
      this.status = OrderStatus.CREATED;
      this.createdAt = Timestamp.now();
      this.updatedAt = this.createdAt;
      this.totalAmount = calculateTotal();
      validateState();
      }
      /**
      * 工厂方法 - 创建新订单
      * 输入校验:参数基础验证
      */
      public static Order create(CustomerId customerId, List<OrderItem> items) {
        if (items == null || items.isEmpty()) {
        throw new DomainException("订单必须包含至少一个订单项");
        }
        return new Order(OrderId.generate(), customerId, items);
        }
        /**
        * 添加订单项 - 核心业务逻辑
        * 职责:订单项管理,状态验证,金额重算
        */
        public void addItem(Product product, Quantity quantity) {
        // 业务规则验证
        if (!status.canModify()) {
        throw new OrderModificationException("当前订单状态不允许修改");
        }
        if (!product.isAvailable(quantity)) {
        throw new InsufficientStockException(product.getId(), quantity);
        }
        // 创建订单项
        OrderItem newItem = OrderItem.create(product.getId(), quantity, product.getPrice());
        items.add(newItem);
        // 更新状态
        recalculateTotal();
        updatedAt = Timestamp.now();
        // 发布领域事件
        DomainEventPublisher.publish(new OrderItemAddedEvent(id, newItem));
        }
        /**
        * 批准订单 - 状态转换业务逻辑
        */
        public void approve() {
        // 前置条件验证
        if (status != OrderStatus.CREATED) {
        throw new IllegalStateException("只能从创建状态批准订单");
        }
        if (items.isEmpty()) {
        throw new EmptyOrderException("空订单不能被批准");
        }
        // 状态转换
        this.status = OrderStatus.APPROVED;
        this.updatedAt = Timestamp.now();
        // 发布领域事件
        DomainEventPublisher.publish(new OrderApprovedEvent(id, totalAmount));
        }
        /**
        * 取消订单 - 业务规则验证
        */
        public void cancel() {
        if (!status.canCancel()) {
        throw new OrderCancellationException("当前订单状态不允许取消");
        }
        this.status = OrderStatus.CANCELLED;
        this.updatedAt = Timestamp.now();
        DomainEventPublisher.publish(new OrderCancelledEvent(id));
        }
        /**
        * 业务计算逻辑 - 封装在领域层
        */
        private void recalculateTotal() {
        this.totalAmount = items.stream()
        .map(OrderItem::calculateSubtotal)
        .reduce(Money.ZERO, Money::add);
        }
        /**
        * 聚合内部一致性验证 - 领域层职责
        */
        private void validateState() {
        if (items.isEmpty()) {
        throw new DomainException("订单必须包含至少一个订单项");
        }
        if (totalAmount.isNegative()) {
        throw new DomainException("订单总金额不能为负数");
        }
        // 验证所有订单项
        items.forEach(OrderItem::validateState);
        }
        /**
        * 业务查询方法
        */
        public boolean containsProduct(ProductId productId) {
        return items.stream()
        .anyMatch(item -> item.getProductId().equals(productId));
        }
        public boolean canBeModified() {
        return status.canModify();
        }
        // 值对象getter,保护内部状态
        public OrderId getId() { return id; }
        public CustomerId getCustomerId() { return customerId; }
        public OrderStatus getStatus() { return status; }
        public Money getTotalAmount() { return totalAmount; }
        public List<OrderItem> getItems() {
          return Collections.unmodifiableList(items);
          }
          }
          /**
          * 领域层异常体系
          */
          public class DomainException extends RuntimeException {
          public DomainException(String message) {
          super(message);
          }
          }
          public class OrderModificationException extends DomainException {
          public OrderModificationException(String message) {
          super(message);
          }
          }
          public class InsufficientStockException extends DomainException {
          public InsufficientStockException(ProductId productId, Quantity quantity) {
          super(String.format("产品%s库存不足,请求数量: %d", productId, quantity.getValue()));
          }
          }

3.3 应用层协调

/**
* 订单应用服务 - 协调领域对象和外部依赖
* 职责:用例编排、事务管理、异常转换
*/
@Service
@Transactional
public class OrderApplicationService {
private final OrderRepository orderRepository;
private final CustomerRepository customerRepository;
private final ProductRepository productRepository;
private final InventoryService inventoryService;
private final DomainEventPublisher eventPublisher;
/**
* 创建订单用例
* 应用层职责:输入验证、流程协调、异常处理
*/
public OrderResult createOrder(CreateOrderCommand command) {
try {
// 1. 命令参数验证
validateCommand(command);
// 2. 加载依赖领域对象
Customer customer = customerRepository.findById(command.getCustomerId())
.orElseThrow(() -> new CustomerNotFoundException(command.getCustomerId()));
// 3. 验证业务规则
if (!customer.isActive()) {
return OrderResult.rejected("客户状态不活跃");
}
// 4. 创建订单项并验证库存
List<OrderItem> orderItems = createOrderItemsWithValidation(command.getItems());
  // 5. 创建领域对象(核心业务逻辑)
  Order order = Order.create(command.getCustomerId(), orderItems);
  // 6. 预留库存(外部系统调用)
  inventoryService.reserveInventory(order.getId(), command.getItems());
  // 7. 持久化领域对象
  orderRepository.save(order);
  return OrderResult.success(order.getId());
  } catch (DomainException e) {
  // 领域异常直接抛出
  throw e;
  } catch (Exception e) {
  // 技术异常转换为应用层异常
  throw new ApplicationException("创建订单失败", e);
  }
  }
  /**
  * 批准订单用例
  */
  public void approveOrder(OrderId orderId) {
  Order order = orderRepository.findById(orderId)
  .orElseThrow(() -> new OrderNotFoundException(orderId));
  try {
  // 调用领域对象业务方法
  order.approve();
  orderRepository.save(order);
  } catch (IllegalStateException e) {
  // 转换领域异常为业务异常
  throw new BusinessException("订单批准失败: " + e.getMessage(), e);
  }
  }
  /**
  * 应用层输入验证
  */
  private void validateCommand(CreateOrderCommand command) {
  if (command == null) {
  throw new IllegalArgumentException("创建订单命令不能为空");
  }
  if (command.getCustomerId() == null) {
  throw new IllegalArgumentException("客户ID不能为空");
  }
  if (command.getItems() == null || command.getItems().isEmpty()) {
  throw new IllegalArgumentException("订单项不能为空");
  }
  command.getItems().forEach(item -> {
  if (item.getProductId() == null) {
  throw new IllegalArgumentException("产品ID不能为空");
  }
  if (item.getQuantity() == null || item.getQuantity().getValue() <= 0) {
  throw new IllegalArgumentException("订单项数量必须大于0");
  }
  });
  }
  private List<OrderItem> createOrderItemsWithValidation(List<OrderItemRequest> requests) {
    return requests.stream()
    .map(request -> {
    Product product = productRepository.findById(request.getProductId())
    .orElseThrow(() -> new ProductNotFoundException(request.getProductId()));
    if (!product.isAvailable(request.getQuantity())) {
    throw new InsufficientStockException(product.getId(), request.getQuantity());
    }
    return OrderItem.create(
    product.getId(),
    request.getQuantity(),
    product.getPrice()
    );
    })
    .collect(Collectors.toList());
    }
    }

3.4 基础设施层实现

/**
* 订单仓储实现 - 基础设施层
* 职责:领域对象持久化,技术细节封装
*/
@Repository
public class JpaOrderRepository implements OrderRepository {
private final OrderJpaRepository jpaRepository;
private final OrderMapper orderMapper;
@Override
public Optional<Order> findById(OrderId orderId) {
  return jpaRepository.findById(orderId.getValue())
  .map(orderMapper::toDomain);
  }
  @Override
  public Order save(Order order) {
  // 发布领域事件
  order.getDomainEvents().forEach(event -> {
  // 事件持久化或发送到消息队列
  eventPublisher.publish(event);
  });
  order.clearDomainEvents();
  // 持久化领域对象
  OrderEntity entity = orderMapper.toEntity(order);
  OrderEntity saved = jpaRepository.save(entity);
  return orderMapper.toDomain(saved);
  }
  @Override
  public void delete(Order order) {
  jpaRepository.deleteById(order.getId().getValue());
  }
  }
  /**
  * 领域事件发布器
  */
  @Component
  public class SpringDomainEventPublisher implements DomainEventPublisher {
  private final ApplicationEventPublisher eventPublisher;
  @Override
  public void publish(DomainEvent event) {
  eventPublisher.publishEvent(event);
  }
  }

3.5 接口层实现

/**
* 订单REST控制器 - 接口层
* 职责:HTTP协议处理、输入校验、响应封装
*/
@RestController
@RequestMapping("/api/v1/orders")
@Validated
public class OrderController {
private final OrderApplicationService orderService;
@PostMapping
public ResponseEntity<ApiResponse<OrderResponse>> createOrder(
  @Valid @RequestBody CreateOrderRequest request) {
  try {
  // 转换为应用层命令
  CreateOrderCommand command = toCommand(request);
  // 调用应用服务
  OrderResult result = orderService.createOrder(command);
  // 构建响应
  return ResponseEntity.ok(ApiResponse.success(toResponse(result)));
  } catch (IllegalArgumentException e) {
  return ResponseEntity.badRequest()
  .body(ApiResponse.error(ErrorCode.INVALID_PARAMETER, e.getMessage()));
  } catch (BusinessException e) {
  return ResponseEntity.badRequest()
  .body(ApiResponse.error(ErrorCode.BUSINESS_ERROR, e.getMessage()));
  } catch (Exception e) {
  log.error("创建订单系统异常", e);
  return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
  .body(ApiResponse.error(ErrorCode.SYSTEM_ERROR, "系统繁忙"));
  }
  }
  private CreateOrderCommand toCommand(CreateOrderRequest request) {
  List<OrderItemRequest> itemRequests = request.getItems().stream()
    .map(this::toItemRequest)
    .collect(Collectors.toList());
    return new CreateOrderCommand(
    CustomerId.of(request.getCustomerId()),
    itemRequests
    );
    }
    }

4. 对比:如果选择数据驱动架构

4.1 数据驱动实现(对比参考)

/**
* 贫血实体 - 数据驱动架构
* 只有数据,没有行为
*/
@Entity
@Table(name = "orders")
public class OrderEntity {
@Id
private Long id;
private String orderNumber;
private String status;
private Long customerId;
private BigDecimal totalAmount;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItemEntity> items;
  // 只有getter/setter
  public Long getId() { return id; }
  public void setId(Long id) { this.id = id; }
  public String getStatus() { return status; }
  public void setStatus(String status) { this.status = status; }
  // 没有业务方法,业务逻辑在Service中
  }
  /**
  * 服务层 - 包含所有业务逻辑
  */
  @Service
  public class OrderService {
  public void createOrder(CreateOrderDto dto) {
  // 参数验证
  validateCreateOrder(dto);
  // 业务逻辑都在Service中
  CustomerEntity customer = customerRepository.findById(dto.getCustomerId());
  if (!"ACTIVE".equals(customer.getStatus())) {
  throw new BusinessException("客户不活跃");
  }
  // 创建实体对象
  OrderEntity order = new OrderEntity();
  order.setId(generateId());
  order.setStatus("CREATED");
  order.setCustomerId(dto.getCustomerId());
  // 计算金额
  BigDecimal total = BigDecimal.ZERO;
  for (OrderItemDto itemDto : dto.getItems()) {
  ProductEntity product = productRepository.findById(itemDto.getProductId());
  if (product.getStock() < itemDto.getQuantity()) {
  throw new BusinessException("库存不足");
  }
  OrderItemEntity item = new OrderItemEntity();
  item.setProductId(itemDto.getProductId());
  item.setQuantity(itemDto.getQuantity());
  item.setUnitPrice(product.getPrice());
  BigDecimal subtotal = product.getPrice().multiply(
  BigDecimal.valueOf(itemDto.getQuantity()));
  total = total.add(subtotal);
  order.getItems().add(item);
  }
  order.setTotalAmount(total);
  orderRepository.save(order);
  }
  public void approveOrder(Long orderId) {
  OrderEntity order = orderRepository.findById(orderId);
  // 业务规则验证在Service中
  if (!"CREATED".equals(order.getStatus())) {
  throw new BusinessException("只能批准创建状态的订单");
  }
  if (order.getItems().isEmpty()) {
  throw new BusinessException("空订单不能批准");
  }
  order.setStatus("APPROVED");
  orderRepository.save(order);
  }
  }

5. 架构选择指南

5.1 何时选择领域驱动架构

选择领域驱动架构当:

  • 业务逻辑复杂,有大量业务规则
  • 系统需要长期演进和维护
  • 团队熟悉DDD概念和方法
  • 需要清晰的业务边界和上下文划分
  • 业务变更频繁,需要快速响应

优势:

  • 业务逻辑集中,易于理解和维护
  • 更好的可测试性
  • 业务意图明确,代码即文档
  • 适应业务变化能力强

5.2 何时选择数据驱动架构

选择数据驱动架构当:

  • 主要是CRUD操作,业务逻辑简单
  • 需要快速交付和上线
  • 团队规模小,技术能力有限
  • 系统以报表和查询为主
  • 业务稳定,变更较少

优势:

  • 开发速度快
  • 学习成本低
  • 适合简单场景
  • 技术栈成熟

5.3 决策矩阵

考虑因素领域驱动架构数据驱动架构
业务复杂度
开发速度
维护成本
团队技能需要培训普遍掌握
系统寿命长期短期
测试便利性

6. 总结

在订单系统这个案例中,我们选择了领域驱动架构,因为:

  1. 业务复杂性:订单处理涉及复杂的状态转换和业务规则
  2. 长期演进:电商系统需要持续迭代和功能扩展
  3. 团队能力:具备实施DDD的技术能力
  4. 维护性:清晰的业务边界降低维护成本

核心收获

  • 领域驱动架构将业务逻辑封装在领域层,使代码更贴近业务语言
  • 数据驱动架构适合简单场景,开发效率高
  • 架构选择应该基于业务需求、团队能力和系统目标
  • 没有最好的架构,只有最适合的架构

通过这个完整的案例,你可以根据自己项目的具体情况,做出正确的架构选择决策。

posted @ 2025-11-20 19:42  gccbuaa  阅读(6)  评论(0)    收藏  举报