企业级订单系统架构设计:领域驱动 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 核心领域实现

java 复制代码
/**
 * 订单聚合根 - 领域驱动设计核心
 * 职责:封装订单相关业务逻辑和状态管理
 */
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 应用层协调

java 复制代码
/**
 * 订单应用服务 - 协调领域对象和外部依赖
 * 职责:用例编排、事务管理、异常转换
 */
@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 基础设施层实现

java 复制代码
/**
 * 订单仓储实现 - 基础设施层
 * 职责:领域对象持久化,技术细节封装
 */
@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 接口层实现

java 复制代码
/**
 * 订单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 数据驱动实现(对比参考)

java 复制代码
/**
 * 贫血实体 - 数据驱动架构
 * 只有数据,没有行为
 */
@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. 维护性:清晰的业务边界降低维护成本

核心收获

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

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

相关推荐
WangMing_X3 小时前
C#上位机软件:2.5 体验CLR实现多语言混合编程
java·开发语言·c#
青云交4 小时前
Java 大视界 -- Java 大数据在智慧交通停车场智能管理与车位预测中的应用实践
java·数据采集·数据清洗·智慧交通·停车场智能管理·智能收费系统·车位预测
豐儀麟阁贵4 小时前
4.4数组的基本操作
java·开发语言·数据结构·算法
组合缺一4 小时前
全球首个支持 IETF JSONPath (RFC 9535) 标准的 Java 框架,Snack4-Jsonpath v4.0.0 发布
java·开发语言·json·jsonpath
智海观潮4 小时前
JVM垃圾回收器、内存分配与回收策略
java·大数据·jvm
vx Biye_Design4 小时前
servlet宠物医院管理系统-计算机毕业设计源码77418
java·vue.js·spring·servlet·eclipse·mybatis
程序员小凯4 小时前
Spring Boot API文档与自动化测试详解
java·spring boot·后端
照物华4 小时前
构建优雅的 Spring Boot Starter:Bean 注册与属性绑定的两大机制
java·spring boot
pcm1235674 小时前
内置线程池的核心参数分析配置
java·开发语言