Java DDD分层架构实战:从理论到代码落地
1. DDD分层架构的本质与价值第一次接触DDD分层架构时我盯着那个四层结构图看了整整半小时。当时刚做完一个电商促销系统Service层堆了2000多行代码各种if-else嵌套看得人头皮发麻。直到把业务逻辑按照DDD分层重新梳理后才真正明白什么是高内聚低耦合。DDD分层不是简单的代码目录划分而是一种思维方式的重构。传统三层架构像是个大仓库所有货物胡乱堆在一起而DDD分层则像智能仓储系统每个区域有明确标识和专用通道。举个例子处理订单支付时传统架构可能直接在Controller里调支付接口而DDD会严格遵循接口层接收支付请求应用层协调支付领域服务领域层执行支付规则校验基础设施层调用第三方支付API这种分工带来的直接好处是当支付渠道从支付宝切换到微信时你只需要修改基础设施层的支付网关实现领域层的核心校验规则完全不用动。去年我们系统接入跨境支付时这种架构设计让海外支付模块的开发效率提升了40%。2. 电商订单系统的分层实战2.1 领域层设计精髓订单系统的核心在于订单聚合根的设计。这个聚合根就像是个大家长管着订单项、支付记录、物流信息等一众家庭成员。来看个典型错误案例// 反例贫血模型的订单类 public class Order { private Long id; private ListOrderItem items; // 只有getter/setter } // 正确做法充血模型的聚合根 public class Order { private OrderId id; private ListOrderItem items; private OrderStatus status; // 领域行为添加订单项 public void addItem(Product product, int quantity) { if (status ! OrderStatus.DRAFT) { throw new IllegalStateException(仅草稿订单可修改); } items.add(new OrderItem(product, quantity)); } // 领域行为提交订单 public void submit() { if (items.isEmpty()) { throw new IllegalStateException(订单项不能为空); } this.status OrderStatus.SUBMITTED; DomainEventPublisher.publish(new OrderSubmittedEvent(this)); } }踩过的坑曾经有个项目把库存校验放在应用层结果出现并发超卖。后来把库存扣减逻辑下沉到领域层的Order聚合根用Version乐观锁彻底解决问题。2.2 应用层的正确打开方式应用层就像交响乐指挥协调各个领域对象完成业务用例。关键要记住它不应该包含任何业务规则。比如创建订单Service RequiredArgsConstructor public class OrderApplicationService { private final OrderRepository orderRepo; private final InventoryService inventoryService; private final PaymentGateway paymentGateway; Transactional public OrderDTO createOrder(CreateOrderCommand command) { // 1. 初始化订单聚合 Order order new Order(command.getUserId()); // 2. 添加订单项会触发库存校验 command.getItems().forEach(item - order.addItem(item.getProductId(), item.getQuantity())); // 3. 提交订单触发领域事件 order.submit(); // 4. 持久化 Order savedOrder orderRepo.save(order); // 5. 调用支付基础设施 PaymentResult result paymentGateway.createPayment( savedOrder.getId(), savedOrder.getTotalAmount()); return OrderAssembler.toDTO(savedOrder, result); } }实测建议应用服务方法最好控制在100行以内。如果超过这个数很可能把领域逻辑错误地上移了。3. 分层架构的黄金法则3.1 依赖倒置的实战技巧基础设施层实现领域接口时有个精妙的技巧——依赖倒置。我们通过JPA实现订单仓储来说明// 领域层定义接口 public interface OrderRepository { Order findById(OrderId id); void save(Order order); } // 基础设施层实现与具体ORM解耦 Repository public class JpaOrderRepository implements OrderRepository { private final EntityManager em; Override public Order findById(OrderId id) { OrderEntity entity em.find(OrderEntity.class, id.getValue()); return OrderMapper.toDomain(entity); } Override public void save(Order order) { OrderEntity entity OrderMapper.toEntity(order); if (em.contains(entity)) { em.merge(entity); } else { em.persist(entity); } } }这个设计带来的灵活性令人惊喜当我们需要添加Redis缓存时只需新建一个CachedOrderRepository装饰器完全不用修改领域层代码。3.2 异常处理的艺术分层架构中异常处理要遵循谁的孩子谁抱走原则领域层抛DomainException如库存不足基础设施层抛InfrastructureException如数据库连接失败应用层统一转换为ApplicationExceptionRestControllerAdvice public class GlobalExceptionHandler { ExceptionHandler(DomainException.class) public ResponseEntityErrorResponse handleDomainException(DomainException e) { return ResponseEntity.badRequest() .body(new ErrorResponse(e.getCode(), e.getMessage())); } ExceptionHandler(InfrastructureException.class) public ResponseEntityErrorResponse handleInfraException() { return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE) .body(new ErrorResponse(SYS_ERROR, 系统繁忙)); } }4. 从单体到微服务的演进刚开始实施DDD时建议先用模块化单体练手。我们的电商系统就是这样演进的初期单模块src/ ├── main/ │ ├── java/ │ │ └── com/ │ │ └── ecommerce/ │ │ ├── order/ │ │ ├── product/ │ │ └── user/成熟后拆分子模块ecommerce/ ├── order-service/ ├── product-service/ └── user-service/关键经验先做好限界上下文划分再考虑物理拆分。曾经有个团队没理清订单和库存的上下文边界就匆忙拆微服务结果RPC调用量暴涨导致系统瘫痪。5. 常见陷阱与解决方案陷阱1DTO贫血症症状接口层和应用层之间传递的DTO只有getter/setter 药方在DTO中加入行为方法public class OrderDTO { private ListOrderItemDTO items; public BigDecimal getTotalAmount() { return items.stream() .map(OrderItemDTO::getSubTotal) .reduce(BigDecimal.ZERO, BigDecimal::add); } }陷阱2领域服务膨胀症状一个领域服务类超过500行代码 药方按聚合根拆分服务比如把OrderService拆成OrderPricingService定价逻辑OrderValidationService校验规则OrderLifecycleService状态转换陷阱3基础设施泄漏症状领域对象中出现Table、Column等注解 药方采用数据模型转换器模式public class OrderMapper { public static Order toDomain(OrderEntity entity) { return new Order( new OrderId(entity.getId()), entity.getItems().stream() .map(OrderItemMapper::toDomain) .collect(toList()) ); } }在最近的一个跨境支付项目中我们严格遵循DDD分层架构使得核心支付逻辑与渠道适配完全解耦。当新增加密货币支付时开发周期从预估的3周缩短到4天。这让我深刻体会到好的架构不是约束而是解放生产力的利器。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2447188.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!