六边形架构(Hexagonal)和洋葱架构(Onion)是《分层架构》的进化,核心都是依赖倒置——解决"业务依赖数据库"这个老问题。这篇把两者讲清楚:它们怎么把依赖方向反过来、以及它俩的共同点和区别。
一、解决什么问题:业务被数据库绑架
回顾分层架构的问题:
@Service
public class OrderService {
private final OrderMapper orderMapper; // 业务层直接依赖持久层
// 业务逻辑里到处是 Mapper,想换数据库、想脱离数据库测试,都难
}依赖方向是"业务 → 数据库"。业务层被数据库"绑架"了。
六边形和洋葱的思路:把依赖方向反过来——业务核心在中心,数据库、框架、UI 全在外圈,外圈依赖中心,中心绝不依赖外圈。
二、核心:依赖倒置 + 端口适配器
关键动作是依赖倒置:业务核心定义"我要什么接口"(端口),外部去实现它(适配器)。
// ① 端口(Port):业务核心定义的接口,属于领域层
public interface OrderRepository { // 业务说"我要能查订单、存订单"
Order findById(Long id);
void save(Order order);
}
// ② 业务核心:只依赖接口,不依赖任何数据库
public class OrderService {
private final OrderRepository repository; // 依赖接口,不是 MySQL 实现
public Order getOrder(Long id) { return repository.findById(id); }
}
// ③ 适配器(Adapter):基础设施层实现接口
@Repository
public class MySqlOrderRepository implements OrderRepository {
public Order findById(Long id) { /* MySQL 具体查询 */ }
}
// 想换 PostgreSQL?再写一个适配器,业务代码一行不动
public class PostgresOrderRepository implements OrderRepository { ... }现在依赖方向反过来了:MySQL 实现依赖 OrderRepository 接口(业务定义的),而不是业务依赖 MySQL。换数据库 = 换一个适配器,业务核心零改动。
三、六边形架构(端口 + 适配器)
六边形架构的名字来自它的形状——业务核心在六边形中心,外部系统通过"端口 + 适配器"接入:
- 端口(Port):业务核心定义的接口(如
OrderRepository、PaymentGateway) - 适配器(Adapter):外部的具体实现(MySQL 实现、HTTP 接口、消息队列)
用代码看就清楚了:
// 端口(Port):业务核心定义的接口,属于领域层
public interface OrderRepository { // 输出端口:业务要"存取订单"
Order findById(Long id);
void save(Order order);
}
public interface PaymentGateway { // 输出端口:业务要"收款"
void pay(Money amount);
}
// 业务核心(六边形中心):只依赖端口,完全不知道外部是谁
public class OrderService {
private final OrderRepository repository; // 依赖端口,不依赖 MySQL
private final PaymentGateway payment; // 依赖端口,不依赖支付宝
public void payOrder(Long id) {
Order order = repository.findById(id); // 通过端口拿数据
order.pay();
payment.pay(order.getAmount()); // 通过端口收款
repository.save(order); // 通过端口存数据
}
}
// 适配器(Adapter):外部系统实现端口
public class MySqlOrderRepository implements OrderRepository { ... } // MySQL 适配器
public class AlipayGateway implements PaymentGateway { ... } // 支付宝适配器
public class WechatGateway implements PaymentGateway { ... } // 微信适配器注意 OrderService 只依赖两个接口(端口),不知道 MySQL、支付宝的存在——想换支付方式,再写一个 WechatGateway 适配器就行,业务核心一行不动。这就是"数据库和框架都被挡在六边形外"的意思。
四、洋葱架构(同心圆)
洋葱架构是六边形的"细化版",把业务核心再分成几层,像洋葱一样一层层:
中心:领域模型(实体/值对象/聚合)
第二层:领域服务(业务规则)
第三层:应用服务(用例编排)
外圈:基础设施(DB、框架、UI)四层用代码看长这样:
// ① 中心:领域模型(纯业务对象,不 import 任何框架类)
package com.demo.order.domain;
public class Order {
private Long id;
private OrderStatus status;
public void pay() { // 业务规则在实体里
if (status != OrderStatus.PENDING) throw new IllegalStateException("状态不对");
this.status = OrderStatus.PAID;
}
}
// ② 第二层:领域服务(跨实体的业务规则)
public class OrderDomainService {
public void deductStock(Order order, Inventory inventory) { ... } // 扣库存
}
// ③ 第三层:应用服务(用例编排 + 事务边界)
package com.demo.order.application;
public class OrderAppService {
public void payOrder(Long id) {
Order order = orderRepository.findById(id); // 查
order.pay(); // 业务规则(领域模型)
orderRepository.save(order); // 存
}
}
// ④ 外圈:基础设施(实现内圈定义的端口 + 框架接入)
package com.demo.order.infrastructure;
@Repository
public class MySqlOrderRepository implements OrderRepository { ... } // 实现内圈接口判定的口诀:内圈(领域模型/领域服务/应用服务)是纯 Java,import 里只有 java.* 和自己项目的类;@Repository、Spring、MySQL 这些只出现在最外圈。洋葱和六边形本质是同一个思想(依赖倒置 + 业务核心独立),只是洋葱把"业务核心"内部又分层了。
五、三者对比
| 维度 | 分层架构 | 六边形/洋葱架构 |
|---|---|---|
| 依赖方向 | 业务 → 数据库 | 数据库 → 业务(倒置) |
| 业务核心 | 依赖框架和 DB | 纯 Java 对象,独立 |
| 换数据库 | 要改业务代码 | 换适配器,业务零改动 |
| 测试 | 要连数据库 | 业务可脱离数据库单测 |
一句话:分层架构是"业务依赖数据库的具体实现",六边形/洋葱让"业务和数据库都依赖抽象(业务定义的接口)"——依赖方向一变,业务核心就自由了。
六、落地(Spring 里怎么用)
Spring 里落地依赖倒置很简单:接口定义在领域包,实现放基础设施包:
com.demo.order
├── domain // 领域核心:接口 + 业务对象
│ ├── Order.java
│ └── OrderRepository.java // 端口(接口)
├── application // 应用服务
│ └── OrderService.java // 依赖 OrderRepository 接口
└── infrastructure // 基础设施
└── MySqlOrderRepository.java // 适配器(实现)OrderService 只依赖 domain 包的接口,infrastructure 包依赖 domain——依赖永远指向 domain(中心)。
小结
- 六边形/洋葱用"依赖倒置"解决分层架构"业务依赖数据库"的问题
- 核心:业务定义端口(接口),外部实现适配器,依赖指向中心
- 六边形 = 端口 + 适配器;洋葱 = 六边形的细化(业务核心再分层)
- 落地:接口放 domain 包,实现放 infrastructure 包
这条线往下走就是《Clean Architecture 整洁架构》——Robert Martin 把它系统化成了"四层同心圆 + 依赖规则",讲得更完整、更严格。
