Appearance
六边形架构(Hexagonal Architecture)
要理解六边形架构(Hexagonal Architecture),我们需要先回到它的设计初心:解决传统架构中“业务逻辑与技术实现紧耦合”的痛点,让领域模型(核心业务)成为系统的“心脏”,而外部依赖(如数据库、API、UI)则作为“外围设备”,通过抽象接口(端口)和适配层与核心连接。
一、六边形架构的核心概念
六边形架构由Alistair Cockburn在2005年提出,别名端口-适配器架构(Ports and Adapters Architecture)。它的核心逻辑可以用三个关键词概括:
1. 核心(Core):领域层(Domain Layer)
六边形的中心是系统的业务核心,包含:
- 领域模型(Entities):如
Order、Product等业务对象,封装了业务属性和规则(如“订单金额必须大于0”)。 - 业务逻辑(Use Cases):如“创建订单”“取消订单”等具体业务流程,是领域模型的行为扩展。
核心的唯一原则:不依赖任何外部系统或技术框架(如Spring、MySQL、HTTP)。它只关心“业务是什么”,不关心“如何实现”。
2. 端口(Ports):核心与外部的抽象边界
端口是核心与外部系统之间的契约接口,分为两类:
输入端口(Ingress Ports):核心暴露给外部的“入口”,用于触发核心业务逻辑。
比如:CreateOrderUseCase接口(定义“创建订单”的输入参数和返回值)、CancelOrderUseCase接口。
外部系统(如UI、API)通过调用输入端口来触发核心逻辑。输出端口(Egress Ports):核心定义的“依赖抽象”,用于调用外部系统(如数据库、第三方服务)。
比如:OrderRepository(定义“保存订单”“查询订单”的方法)、InventoryService(定义“验证库存”的方法)。
核心通过输出端口“声明”自己需要的外部能力,但不关心这些能力的具体实现。
3. 适配器(Adapters):端口与外部系统的“翻译器”
适配器是端口的具体实现,负责将外部系统的“语言”转换为核心能理解的“语言”,反之亦然。它分为两类:
输入适配器(Ingress Adapters):将外部请求转换为核心的输入参数,调用输入端口。
比如:- HTTP适配器(Spring MVC Controller):接收
POST /orders请求,将JSON参数转换为CreateOrderRequest对象,调用CreateOrderUseCase。 - CLI适配器:处理命令行输入,触发核心逻辑。
- HTTP适配器(Spring MVC Controller):接收
输出适配器(Egress Adapters):实现输出端口,将核心的调用转换为外部系统的操作。
比如:- JDBC适配器:实现
OrderRepository,用SQL将Order对象保存到MySQL。 - REST适配器:实现
InventoryService,调用第三方库存服务的GET /inventory/{productId}接口。
- JDBC适配器:实现
二、六边形架构的结构示意
外部系统(Web/CLI/数据库/第三方服务)
│
▼
适配器层(输入/输出适配器)
│
▼
端口层(输入/输出端口,抽象接口)
│
▼
核心层(领域模型+业务逻辑,纯业务)
三、六边形架构的关键优势
对比传统的三层架构(UI→业务层→数据层)或分层架构,六边形架构的优势更突出:
1. 业务逻辑“绝对纯净”
核心层不依赖任何技术细节(如Spring的@Autowired、MySQL的JDBC),只关注业务规则。即使外部系统(如数据库从MySQL换成PostgreSQL)或技术框架(如从Spring换成Quarkus)变化,核心层无需修改。
2. 易测试性
核心层的测试可以脱离外部依赖:
- 测试
CreateOrderUseCase时,只需MockOrderRepository和InventoryService(输出端口的实现),无需启动数据库或第三方服务。 - 适配器层的测试只需验证“转换逻辑”(如HTTP参数是否正确转成
CreateOrderRequest),无需测试业务逻辑。
3. 灵活性与扩展性
- 新增外部系统:只需新增适配器,无需修改核心。比如要支持“消息队列触发订单创建”,只需新增
MQAdapter(输入适配器),调用CreateOrderUseCase。 - 替换外部系统:只需替换适配器,核心层不变。比如把“库存服务从REST换成gRPC”,只需修改
InventoryServiceAdapter的实现,核心的CreateOrderUseCase无需改动。
4. 团队协作效率提升
- 领域团队(Business Team):专注于核心层的领域模型和业务逻辑,无需关心技术实现。
- 技术团队(Tech Team):专注于适配器层的技术实现(如HTTP、数据库、消息队列),无需理解复杂的业务规则。
四、六边形架构的实践案例:电商订单系统
我们用一个简化的“创建订单”流程,说明各层的职责:
1. 核心层(Domain Layer)
- 领域模型:
Order(订单ID、用户ID、商品列表、金额)、Product(商品ID、名称、价格)。 - 输入端口:
CreateOrderUseCase(定义输入CreateOrderRequest,输出OrderResult)。javapublic interface CreateOrderUseCase { OrderResult createOrder(CreateOrderRequest request); } - 输入端口实现:
CreateOrderUseCaseImpl(核心业务逻辑)。javapublic class CreateOrderUseCaseImpl implements CreateOrderUseCase { // 依赖输出端口(抽象) private final OrderRepository orderRepository; private final InventoryService inventoryService; // 构造器注入(依赖倒置) public CreateOrderUseCaseImpl(OrderRepository orderRepository, InventoryService inventoryService) { this.orderRepository = orderRepository; this.inventoryService = inventoryService; } @Override public OrderResult createOrder(CreateOrderRequest request) { // 1. 验证请求参数(业务规则:商品列表不能为空) if (request.getProducts().isEmpty()) { throw new IllegalArgumentException("商品列表不能为空"); } // 2. 计算订单总金额(业务规则:金额=商品价格×数量之和) BigDecimal totalAmount = calculateTotalAmount(request.getProducts()); // 3. 验证库存(调用输出端口,不关心具体实现) inventoryService.verifyInventory(request.getProducts()); // 4. 创建Order对象(领域模型) Order order = new Order(request.getUserId(), request.getProducts(), totalAmount); // 5. 保存订单(调用输出端口) orderRepository.save(order); // 6. 返回结果 return new OrderResult(order.getId(), "订单创建成功"); } } - 输出端口:
OrderRepository(保存订单)、InventoryService(验证库存)。javapublic interface OrderRepository { void save(Order order); } public interface InventoryService { void verifyInventory(List<Product> products); }
2. 适配器层(Adapters)
- 输入适配器:
OrderController(HTTP适配器,接收外部请求)。java@RestController @RequestMapping("/orders") public class OrderController { // 依赖输入端口(抽象) private final CreateOrderUseCase createOrderUseCase; @Autowired public OrderController(CreateOrderUseCase createOrderUseCase) { this.createOrderUseCase = createOrderUseCase; } @PostMapping public ResponseEntity<OrderResult> createOrder(@RequestBody CreateOrderRequest request) { // 转换HTTP请求→核心输入参数,调用输入端口 OrderResult result = createOrderUseCase.createOrder(request); return ResponseEntity.ok(result); } } - 输出适配器:
JdbcOrderRepository(数据库适配器,实现OrderRepository)、RestInventoryService(第三方服务适配器,实现InventoryService)。java// JDBC适配器:将核心的save(Order)转换为SQL操作 @Repository public class JdbcOrderRepository implements OrderRepository { private final JdbcTemplate jdbcTemplate; @Autowired public JdbcOrderRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public void save(Order order) { String sql = "INSERT INTO orders (id, user_id, total_amount) VALUES (?, ?, ?)"; jdbcTemplate.update(sql, order.getId(), order.getUserId(), order.getTotalAmount()); // 保存商品列表(省略) } } // REST适配器:将核心的verifyInventory转换为第三方API调用 @Service public class RestInventoryService implements InventoryService { private final RestTemplate restTemplate; @Autowired public RestInventoryService(RestTemplate restTemplate) { this.restTemplate = restTemplate; } @Override public void verifyInventory(List<Product> products) { // 调用第三方库存服务的REST API String url = "https://inventory-service.com/api/inventory/verify"; ResponseEntity<Void> response = restTemplate.postForEntity(url, products, Void.class); if (!response.getStatusCode().is2xxSuccessful()) { throw new InventoryNotEnoughException("库存不足"); } } }
3. 依赖注入(Dependency Injection)
通过Spring的依赖注入,将适配器注入到核心层:
CreateOrderUseCaseImpl依赖OrderRepository和InventoryService,Spring会自动注入它们的适配器实现(JdbcOrderRepository、RestInventoryService)。OrderController依赖CreateOrderUseCase,Spring会注入CreateOrderUseCaseImpl。
五、六边形架构的实践注意事项
要真正发挥六边形架构的价值,需要避免以下常见错误:
1. 核心层不能有外部依赖
核心层不能引入任何技术框架的代码(如Spring的@Component、Jackson的@JsonProperty),也不能依赖具体的外部系统(如MySQL、Redis)。核心层的代码应该是“纯Java/Kotlin”的。
2. 端口设计要“基于业务”,而非“基于技术”
- 输入端口应该对应“业务用例”(如
CreateOrderUseCase),而非“技术接口”(如OrderController)。 - 输出端口应该对应“业务依赖”(如
InventoryService),而非“技术实现”(如RestTemplate)。
3. 适配器层要“薄”,不能包含业务逻辑
适配器的职责只有两个:转换格式、调用端口。业务逻辑必须放在核心层,比如“验证商品列表不能为空”不能放在OrderController,而要放在CreateOrderUseCaseImpl。
4. 避免端口“过细”或“过粗”
- 过细:每个小功能都定义一个端口,导致接口爆炸(如
CreateOrderUseCase、UpdateOrderUseCase、DeleteOrderUseCase可以合并为OrderUseCase吗?不,应该保持用例的单一职责)。 - 过粗:一个端口包含多个不相关的功能(如
OrderRepository包含“保存订单”和“发送消息”,这会违反单一职责)。
六、六边形架构与其他架构的关系
- 与DDD(领域驱动设计)的关系:六边形架构是DDD的“基础设施模式”,帮助实现DDD的“领域层隔离”。DDD关注“如何建模业务”,六边形架构关注“如何组织代码以保护领域层”。
- 与Clean Architecture(整洁架构)的关系:六边形架构是Clean Architecture的“前身”,Clean Architecture更强调“分层”(实体→用例→接口适配器→框架),而六边形架构更强调“端口-适配器”的边界。两者的核心思想一致:依赖倒置、业务逻辑中心化。
七、总结
六边形架构的本质是**“用抽象隔离变化”**:
- 核心层(业务)是“稳定的”,不会因为外部系统变化而修改。
- 端口层(抽象)是“契约”,定义了核心与外部的交互规则。
- 适配器层(实现)是“可变的”,负责对接具体的外部系统。
对于业务复杂、需要长期维护的系统(如电商、金融、医疗),六边形架构能有效降低系统的耦合度,提高代码的可测试性和扩展性。而对于业务简单、快速迭代的系统(如原型、小工具),六边形架构可能会增加不必要的复杂度,需要权衡使用。
一句话概括六边形架构:“业务在中心,技术在周边,抽象做桥梁”。
