一、为什么需要调整分层架构

微服务架构最大的特点就是每个服务可以独立开发、独立部署。但传统的DDD(领域驱动设计)分层架构在设计时,更多考虑的是单体应用的整体性。当我们将DDD应用到微服务中时,会发现一些问题:

  1. 依赖过重:传统分层架构里,基础设施层可能包含数据库、缓存、消息队列等,如果某个服务只想用缓存,却不得不引入整个基础设施层。
  2. 部署耦合:如果领域层和基础设施层绑定太紧,每次数据库变更都可能影响领域逻辑,导致服务无法真正独立部署。
  3. 团队协作问题:微服务通常由不同团队维护,如果分层不清晰,团队之间容易在代码耦合上产生冲突。

举个例子:

// 技术栈:Java + Spring Boot  
// 传统DDD分层结构(问题示例)  
// 领域层直接依赖具体数据库实现(违反依赖倒置原则)  
public class OrderService {
    @Autowired
    private OrderJpaRepository orderRepository; // 直接依赖JPA实现
    
    public void createOrder(Order order) {
        // 业务逻辑与数据库操作混杂
        if (order.isValid()) {
            orderRepository.save(order); 
        }
    }
}

注释:这里领域服务直接依赖了JPA的具体实现,如果换成MongoDB就需要修改领域代码,无法独立部署。

二、调整策略:依赖倒置与适配层

解决上述问题的核心是依赖倒置——让高层模块(领域层)不依赖低层模块(基础设施),而是让低层模块适配高层模块。具体做法:

1. 提取领域接口

在领域层定义仓储接口,基础设施层负责实现:

// 领域层定义接口(不依赖具体实现)
public interface OrderRepository {
    void save(Order order);
}

// 基础设施层提供实现(可以是MySQL、Redis等)
@Repository
public class OrderJpaRepositoryImpl implements OrderRepository {
    @Override
    public void save(Order order) {
        // 具体数据库操作
    }
}

2. 增加防腐层(ACL)

当微服务之间需要调用时,通过防腐层隔离外部服务的变化:

// 技术栈:Java + Feign  
// 防腐层示例:将外部服务"翻译"成领域理解的模型  
public class PaymentServiceAdapter {
    @Autowired
    private PaymentFeignClient paymentClient;
    
    public PaymentStatus queryPayment(String orderId) {
        // 将Feign返回的DTO转换为领域模型
        PaymentDto dto = paymentClient.getPayment(orderId);
        return PaymentStatus.of(dto.getCode());
    }
}

注释:即使支付服务接口变更,只需修改适配层,领域逻辑不受影响。

三、实战:可独立部署的分层设计

以一个订单服务为例,展示完整调整后的结构:

// 技术栈:Java + Spring Boot  
// 调整后的分层架构  
// 1. 领域层(核心业务)  
public class Order {
    private String id;
    private BigDecimal amount;
    
    // 领域方法
    public boolean isValid() {
        return amount.compareTo(BigDecimal.ZERO) > 0;
    }
}

// 2. 应用层(协调领域对象)  
public class OrderAppService {
    private final OrderRepository orderRepo;
    
    public void createOrder(OrderCommand command) {
        Order order = new Order(command.getAmount());
        if (order.isValid()) {
            orderRepo.save(order); // 依赖抽象接口
        }
    }
}

// 3. 基础设施层(实现细节)  
@Repository
public class OrderMongoRepository implements OrderRepository {
    @Override
    public void save(Order order) {
        // MongoDB具体操作
    }
}

关键点

  • 领域层完全不知道数据如何存储
  • 切换数据库只需替换基础设施实现
  • 应用层通过依赖注入绑定接口与实现

四、注意事项与最佳实践

  1. 接口归属权:领域接口(如OrderRepository)必须由领域层定义,而非基础设施层。
  2. 依赖方向:确保所有依赖箭头指向领域层(如下图示意):
    基础设施 → 应用层 → 领域层  
           ↘___________↗  
    
  3. 测试策略
    • 领域层用单元测试,不启动Spring容器
    • 基础设施层用集成测试
  4. 团队约定:严格禁止跨层直接调用(如基础设施层直接访问领域模型)。

五、总结

调整后的分层架构让微服务真正具备:

  • 技术异构性:每个服务可自由选择数据库、框架
  • 独立部署能力:修改数据库不影响领域代码
  • 清晰边界:团队协作时减少“踩坑”概率

就像乐高积木,标准化接口是连接块,具体实现是不同颜色的积木。只要接口不变,内部可以随意替换——这才是微服务想要的灵活性。