本文目录导读:

- 目录导读
- 为什么传统分层架构在微服务时代失效了?
- 洋葱架构核心概念:依赖规则与领域模型
- Java项目中的洋葱架构落地案例(Spring Boot + MyBatis)
- 关键实践:如何隔离基础设施与技术细节
- 常见坑与最佳实践(含代码对比)
- 问答环节:解决你关于洋葱架构的5个高频问题
- 总结:何时该用,何时不该用
Java洋葱架构实战案例:从理论到落地的架构解耦指南
目录导读
- 为什么传统分层架构在微服务时代失效了?
- 洋葱架构核心概念:依赖规则与领域模型
- Java项目中的洋葱架构落地案例(Spring Boot + MyBatis)
- 关键实践:如何隔离基础设施与技术细节
- 常见坑与最佳实践(含代码对比)
- 问答环节:解决你关于洋葱架构的5个高频问题
- 何时该用,何时不该用
为什么传统分层架构在微服务时代失效了?
传统三层架构(Controller → Service → DAO)看似清晰,但实际项目中常出现:
- Service层直接依赖MyBatis的Mapper接口,导致数据库逻辑泄漏到业务层。
- 一旦更换ORM框架(如从MyBatis换到JPA),Service层代码大面积重写。
- 单元测试被迫启动Spring容器,因为Service与基础设施强耦合。
核心痛点:依赖方向失控——高层模块(业务规则)没有保护自己不受低层模块(基础设施)变化的影响。
洋葱架构核心概念:依赖规则与领域模型
洋葱架构(Onion Architecture)由Jeffrey Palermo提出,其本质是依赖倒置原则的极致化,结构从内到外:
- 领域模型(Domain Model):纯Java对象,不依赖任何框架,表达业务状态。
- 领域服务(Domain Service):编排领域对象,处理复杂业务逻辑。
- 应用服务(Application Service,又称用例层):定义用例(如“创建订单”),协调领域服务与外部接口。
- 基础设施(Infrastructure):数据库、消息队列、外部API实现。
铁律:依赖只能由外向内(即外圈依赖内圈),内圈绝不依赖外圈,数据库、Spring、MyBatis都是“细节”,必须可以被替换。
Java项目中的洋葱架构落地案例(Spring Boot + MyBatis)
场景:用户下单扣库存系统。
结构示例(基于Maven多模块):
order-domain (纯Java,无Spring依赖)
order-application (用例服务,依赖domain接口)
order-infrastructure (实现接口,含MyBatis Repository)
order-interfaces (Controller层,依赖application)
代码示例:
领域层(domain):
public class Order {
private OrderId id;
private Money totalAmount;
// 业务方法:计算折扣
public Money applyDiscount(DiscountRate rate) { ... }
}
应用层(application):
public class CreateOrderUseCase {
private final OrderRepository repository; // 接口,定义在domain
private final InventoryGateway inventory; // 接口,定义在application
public Order execute(CreateOrderCommand cmd) {
// 校验、组合领域对象
Order order = new Order(...);
boolean reserved = inventory.reserve(cmd.getProductId());
if (!reserved) throw new BusinessException("库存不足");
return repository.save(order);
}
}
基础设施层(infrastructure):
@Repository
public class MyBatisOrderRepository implements OrderRepository {
private final OrderMapper mapper; // MyBatis接口,仅在该层使用
@Override
public Order save(Order order) {
OrderDO data = OrderConverter.toDO(order); // 转换逻辑仅在此层
mapper.insert(data);
return OrderConverter.toDomain(data);
}
}
关键点:OrderRepository接口定义在domain层,而实现藏在infrastructure层,Service层代码根本不知道MyBatis的存在。
关键实践:如何隔离基础设施与技术细节
- 接口所有权:接口(如
OrderRepository)属于内圈(domain),不属于外圈(infrastructure),这样内圈定义行为,外圈实现细节。 - 使用适配器模式:例如
UserJpaAdapter实现UserPort接口,适配JPA或者MyBatis查询。 - DTO与DO分离:领域对象(Order)与数据对象(OrderDO)拆分,禁止在应用层直接操作
OrderDO。 - 依赖注入方向:Spring仅在
infrastructure和interfaces层使用,domain与application层保持纯POJO。
常见坑与最佳实践(含代码对比)
❌ 错误示范:
@Service
public class OrderService {
@Autowired
private OrderMapper mapper; // 直接依赖外部ORM
public void create(OrderDTO dto) {
OrderDO data = new OrderDO();
// 大量setter,业务逻辑和持久化混淆
mapper.insert(data);
}
}
✅ 正确示范:
@Service
public class OrderApplicationService {
private final CreateOrderUseCase useCase;
public OrderApplicationService(CreateOrderUseCase useCase) {
this.useCase = useCase;
}
// 仅做参数转换,无业务逻辑
}
最佳实践清单:
- 保持领域层纯净:不引入
javax.persistence、org.springframework.stereotype注解。 - 用例粒度:每个用例一个类(如
CreateOrderUseCase),避免服务类爆炸。 - 测试策略:domain和application层用JUnit纯单元测试,不启动Spring。
- 包结构映射:使用
import控制依赖方向,必要时用ArchUnit测试强制依赖规则。
问答环节:解决你关于洋葱架构的5个高频问题
Q1:洋葱架构和六边形架构(端口-适配器)有什么区别? A:两者本质相同,六边形架构更强调“端口”(输入/输出),洋葱架构更强调层级和领域模型为核心,实践中常混用,核心都是依赖倒置。
Q2:我们团队刚转型,迁移成本高吗? A:短期有成本(重写Repository接口),但长期收益大,建议从新模块开始试点,老模块逐步抽取接口。
Q3:MyBatis的@Param注解放在domain层接口里会怎样?
A:不推荐,domain层接口应使用领域原语(如OrderId),@Param属于MyBatis技术细节,应放在infrastructure层实现里。
Q4:是否所有项目都适合洋葱架构? A:不是,对于CRUD极简单的项目(无复杂业务逻辑),洋葱架构会增加类数量,适合业务规则复杂、需要长期演进的中大型系统。
Q5:Controller层属于哪一圈? A:属于最外层(interfaces/UI层),它只负责HTTP协议解析,调用application层用例,不做任何业务判断。
何时该用,何时不该用
该用:
- 业务规则复杂(如金融、电商订单、计费引擎)。
- 需要替换ORM、消息中间件等基础设施。
- 团队希望实现高质量单元测试(不启动Spring)。
不该用:
- 单纯的数据展示后台(增删改查无逻辑)。
- 快速原型验证(MVP阶段)。
- 团队对DDD和依赖倒置不熟悉时,强推会适得其反。
洋葱架构不是银弹,但它能帮你保护核心业务资产,关键是在“内聚领域”与“技术解耦”之间找到平衡点,建议从Order这样的核心用例开始重构,逐步感受架构带来的秩序感。