Spring Boot 3.x实战:用DDD分层架构重构老旧订单系统(附完整代码)

发布时间:2026/5/27 11:20:25

Spring Boot 3.x实战:用DDD分层架构重构老旧订单系统(附完整代码) Spring Boot 3.x实战用DDD分层架构重构老旧订单系统1. 为什么选择DDD分层架构重构订单系统在电商行业快速发展的今天订单系统作为核心业务枢纽其复杂度呈指数级增长。传统三层架构的订单系统往往面临以下典型问题业务逻辑与技术实现高度耦合Service层充斥着SQL语句与业务规则的混合代码贫血模型导致行为缺失Order实体沦为仅有getter/setter的数据容器可维护性持续恶化新需求开发需要同时修改多个层级代码扩展成本居高不下引入新支付方式或物流方案时牵一发而动全身DDD分层架构为解决这些问题提供了系统化的方法论。我们来看一个真实案例某跨境电商平台订单系统在月订单量突破50万后新增促销功能需要2周开发周期而采用DDD重构后同样需求仅需3天。2. Spring Boot 3.x技术栈选型2.1 核心框架组合// pom.xml关键依赖 dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency dependency groupIdorg.mapstruct/groupId artifactIdmapstruct/artifactId version1.5.5.Final/version /dependency技术栈对比表组件类型传统方案DDD重构方案优势比较ORM框架MyBatisSpring Data JPA更好的聚合根支持对象映射手动setterMapStruct编译期生成代码零运行时开销验证框架Hibernate ValidatorJakarta Validation支持Java 17特性事件机制自定义观察者Spring ApplicationEvent内置事务事件相位控制2.2 项目结构规范order-service/ ├── domain/ │ ├── model/ │ │ ├── Order.java # 聚合根 │ │ ├── OrderItem.java # 实体 │ │ └── Address.java # 值对象 │ ├── service/ │ │ └── OrderService.java # 领域服务 │ └── repository/ │ └── OrderRepository.java # 仓储接口 ├── application/ │ ├── command/ │ │ └── CreateOrderCommand.java │ └── service/ │ └── OrderAppService.java └── infrastructure/ ├── persistence/ │ └── OrderRepositoryImpl.java └── messaging/ └── OrderEventPublisher.java提示领域层应保持纯净所有Spring注解仅出现在应用层和基础设施层3. 领域模型深度设计3.1 聚合根设计原则Entity Table(name orders) public class Order extends BaseAggregateRoot { EmbeddedId private OrderId id; Embedded private CustomerInfo customer; ElementCollection CollectionTable(name order_items) private ListOrderItem items; Enumerated(EnumType.STRING) private OrderStatus status; // 领域行为 public void addItem(Product product, int quantity) { this.items.add(OrderItem.create(product, quantity)); this.calculateTotal(); } // 内部方法保持业务完整性 private void calculateTotal() { this.total items.stream() .map(OrderItem::getSubtotal) .reduce(Money.ZERO, Money::add); } }聚合设计要点通过addItem方法而非setter暴露修改入口所有内部状态变更必须通过聚合根方法值对象设计为不可变类集合操作使用防御性拷贝3.2 领域事件实现public class OrderPaidEvent extends DomainEvent { private OrderId orderId; private PaymentId paymentId; private LocalDateTime paidTime; // 事件构造器私有化 private OrderPaidEvent(Order order, Payment payment) { this.orderId order.getId(); this.paymentId payment.getId(); this.paidTime payment.getPaidTime(); } public static OrderPaidEvent of(Order order, Payment payment) { return new OrderPaidEvent(order, payment); } }在应用服务中发布事件Transactional public void confirmPayment(OrderId orderId, Payment payment) { Order order orderRepository.findById(orderId) .orElseThrow(OrderNotFoundException::new); order.confirmPayment(payment); eventPublisher.publish(OrderPaidEvent.of(order, payment)); }4. 分层架构具体实现4.1 用户接口层设计RestController RequestMapping(/api/orders) RequiredArgsConstructor public class OrderController { private final OrderAppService appService; PostMapping public ResponseEntityOrderDTO createOrder( Valid RequestBody CreateOrderRequest request) { CreateOrderCommand command OrderMapper.INSTANCE.toCommand(request); OrderDTO dto appService.createOrder(command); return ResponseEntity.created(URI.create(/orders/ dto.getId())) .body(dto); } }DTO转换最佳实践使用MapStruct实现类型安全转换禁止在DTO中出现领域对象字段校验注解仅出现在DTO上4.2 应用层协调逻辑Service Transactional RequiredArgsConstructor public class OrderAppService { private final OrderService orderService; private final InventoryClient inventoryClient; private final OrderRepository orderRepository; public OrderDTO createOrder(CreateOrderCommand command) { // 1. 库存预占 inventoryClient.reserve(command.getItems()); // 2. 创建订单 Order order orderService.createOrder( command.getCustomerId(), command.getShippingAddress(), command.getItems()); // 3. 持久化 orderRepository.save(order); return OrderMapper.INSTANCE.toDTO(order); } }注意应用服务不应包含if-else等业务判断逻辑这些应下沉到领域层4.3 基础设施层实现JPA持久化示例Repository public class OrderRepositoryImpl implements OrderRepository { private final JpaOrderRepository jpaRepository; Override public Order save(Order order) { OrderEntity entity OrderMapper.INSTANCE.toEntity(order); OrderEntity saved jpaRepository.save(entity); return OrderMapper.INSTANCE.toDomain(saved); } Override public OptionalOrder findById(OrderId id) { return jpaRepository.findById(id.getValue()) .map(OrderMapper.INSTANCE::toDomain); } }Redis缓存装饰器Primary Repository RequiredArgsConstructor public class CachedOrderRepository implements OrderRepository { private final OrderRepository delegate; private final RedisTemplateString, Order redisTemplate; Override public Order save(Order order) { Order saved delegate.save(order); redisTemplate.opsForValue().set( order: saved.getId().getValue(), saved, 30, TimeUnit.MINUTES); return saved; } }5. 重构过程中的关键挑战5.1 数据库迁移策略分阶段迁移方案双写阶段新老系统同时写入用消息队列保证最终一致验证阶段定时任务对比关键数据一致性切流阶段逐步将读请求切换到新系统收尾阶段下线老系统表结构-- 新旧表结构对比示例 CREATE TABLE legacy_orders ( id BIGINT PRIMARY KEY, customer_name VARCHAR(100), total DECIMAL(10,2) ); CREATE TABLE ddd_orders ( id VARCHAR(36) PRIMARY KEY, customer_id VARCHAR(36), total_amount DECIMAL(10,2), total_currency CHAR(3) );5.2 事务一致性保障Saga模式实现Saga public class OrderCreationSaga { private final CommandGateway commandGateway; StartSaga SagaEventHandler(associationProperty orderId) public void handle(OrderCreatedEvent event) { commandGateway.send(new ReserveInventoryCommand( event.getOrderId(), event.getItems() )); } SagaEventHandler(associationProperty orderId) public void handle(InventoryReservedEvent event) { commandGateway.send(new ConfirmOrderCommand( event.getOrderId() )); } }6. 性能优化实践6.1 查询优化方案CQRS实现Repository public interface OrderQueryRepository extends JpaRepositoryOrderView, String { Query(SELECT new com.example.order.query.OrderListView(...) FROM OrderView o WHERE o.customerId :customerId) PageOrderListView findCustomerOrders( Param(customerId) String customerId, Pageable pageable); }N1问题解决EntityGraph(attributePaths {items.product}) Query(SELECT o FROM Order o WHERE o.id :id) OptionalOrder findByIdWithItems(Param(id) OrderId id);6.2 缓存策略设计多级缓存方案缓存层级技术实现缓存时间适用场景L1Caffeine1分钟高频访问的单个订单L2Redis30分钟分布式节点共享数据L3HTTP缓存头24小时不变的基础数据7. 监控与运维改进7.1 关键指标埋点Aspect Component RequiredArgsConstructor public class OrderMetricsAspect { private final MeterRegistry meterRegistry; Around(execution(* com.example.order..*Service.*(..))) public Object measureServicePerformance(ProceedingJoinPoint pjp) { String className pjp.getTarget().getClass().getSimpleName(); String methodName pjp.getSignature().getName(); Timer.Sample sample Timer.start(meterRegistry); try { return pjp.proceed(); } finally { sample.stop(meterRegistry.timer(domain.service, class, className, method, methodName)); } } }7.2 日志规范结构化日志配置logging: pattern: console: %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n json: enabled: true level: org.hibernate.SQL: debug org.hibernate.type.descriptor.sql.BasicBinder: trace8. 完整代码示例领域服务实现Service RequiredArgsConstructor public class OrderServiceImpl implements OrderService { private final OrderValidator validator; private final PricingService pricingService; Override public Order createOrder(CustomerId customerId, ShippingAddress address, ListOrderItem items) { // 验证业务规则 validator.validate(items); // 计算价格 items.forEach(item - item.applyPrice(pricingService.calculatePrice(item))); // 创建聚合根 return Order.create(customerId, address, items); } }值对象持久化Embeddable public class ShippingAddress { Column(name shipping_recipient) private String recipient; Embedded private PostalAddress address; Column(name shipping_phone) private PhoneNumber phone; } // 自定义类型转换器 Converter(autoApply true) public class PhoneNumberConverter implements AttributeConverterPhoneNumber, String { Override public String convertToDatabaseColumn(PhoneNumber attribute) { return attribute null ? null : attribute.getValue(); } Override public PhoneNumber convertToEntityAttribute(String dbData) { return dbData null ? null : new PhoneNumber(dbData); } }

相关新闻