六边形架构以及应用场景
背景
什么情况下适合用六边形架构?
外部系统的集成方式多变的软件系统,比如ISV等。例如我要开发一个智能体,刚开始在**孵化,后面可以跟SAP,Oracle,用友集成。 当我跟其他厂商集成的时候,无需修改业务逻辑代码,只需要增加适配器即可。
如果跟外部系统集成没有多变,用DDD四层架构或者整洁架构即可。
不同ERP厂商外部调用的差异
- WEB端和后端集成差异: 金蝶苍穹使用0代码框架,后端通过Plugin类响应前端的请求,其他ERP可能有其他的集成形式
- 分布式定时任务的框架不同厂商有不同的技术实现
- 跨微服务集成差异:有些公司使用HTTP,有些使用DUBBO Hessian协议,等等。
- 跨系统集成通信协议差异:不同公司会开发不同的集成网关。
- 数据访问方式差异:金蝶苍穹使用自己封装的DB工具操纵数据库,有些公司使用Mybatis,其他公司可能使用其他产品。
- 数据库存储介质差异:金蝶使用PostgreSQL,其他公司可能使用Oracle,Mysql等。
- 日志差异:不同ERP的日志系统不同,需要抽象日志接口,适配各系统的日志机制。
- 监控集成差异:集成Prometheus或自定义健康检查接口,确保Agent在各ERP中的运行状态可追踪。
六边形架构介绍

六边形架构的设计思想源于Alistair Cockburn在2005年提出的“六边形关系图”理论。在这个理论中,软件系统被视为一个六边形,其中有三组组件构成:核心业务逻辑(Domain),输入和输出端口(Ports)以及适配器(Adapters)。这些组件通过一系列接口进行交互,内部的业务逻辑(六边形中心),并通过端口和适配器与外部系统进行交互。
适配器(Adapters) 是连接核心业务逻辑与外部世界(如数据库、用户界面、第三方服务等)的桥梁。它们通过实现端口(Ports)定义的接口,将外部技术细节转化为核心层能理解的业务操作。
与传统分层架构的对比优势

分层

Model
封装核心业务逻辑和状态,无技术依赖(不依赖Spring等)。 按DDD规范要求,业务逻辑在领域层实现。(但是门槛比较高,从过去实施效果来看不太理想,运行业务逻辑写在application层)
Port
定义与外部交互的接口(输入/输出端口)
和外部交互的接口定义,分为输入端口(驱动端口)和输出端口(被驱动端口)。
驱动端口(Driving Ports)输入方向:
外部系统如何与核心业务交互:外部进来的HTTP请求、RPC请求、CLI命令、MQ订阅消息,Schedule(定时任务),在inbound适配器中实现。
被驱动端口(Driven Ports)输出方向:
核心业务需要外部资源支持。数据库访问、RPC调用(调用外部)、文件存储, ES存储,向量数据库等。在outbound适配器中实现。
Application
协调领域逻辑和外部服务,实现业务用例流程 依赖端口接口,不直接操作外部技术
Adapter
实现端口接口,适配具体技术(如 HTTP、数据库) 强技术依赖(Spring、JPA、Feign),定时任务
代码实现

总结:
外部请求 → [左适配器(如HTTP Controller)] → 调用左端口(OrderUseCase) → 应用核心逻辑
↓
外部服务 ← [右适配器(如JPA Repository)] ← 实现右端口(OrderRepository) ← 依赖倒置
输入端口、输入适配器
应用服务(application 层)继承输入端口(接口)
输入适配器通过引用方式依赖输入端口
应用服务通过引用依赖输出端口
输出端口,输出适配器
输出适配器继承输出端口
输入端口
package com.example.demo.port.inbound;
import com.example.demo.port.inbound.dto.OrderRequestDTO;
import com.example.demo.port.inbound.dto.OrderResponseDTO;
public interface OrderUseCase {
OrderResponseDTO placeOrder(OrderRequestDTO request);
}
收入端口实现类--应用服务
package com.example.demo.application;
import com.example.demo.port.inbound.OrderUseCase;
import com.example.demo.port.inbound.dto.OrderRequestDTO;
import com.example.demo.port.inbound.dto.OrderResponseDTO;
import com.example.demo.port.outbound.OrderRepository;
public class OrderServiceImpl implements OrderUseCase {
OrderRepository orderRepository; //通过引用依赖右端口
@Override
public OrderResponseDTO placeOrder(OrderRequestDTO request) {
orderRepository.save(null);
return null;
}
}
输入适配器
完全解耦:OrderController 不直接实现接口,仅作为 HTTP 适配器调用接口。
协议无关性:OrderInputPort 接口不包含任何 HTTP 注解,可复用其他适配器(如消息监听器)。
符合单一职责原则:Controller 仅负责协议转换,业务逻辑集中在 OrderService。
package com.example.demo.adapter.inbound.http;
import com.example.demo.port.inbound.OrderUseCase;
import com.example.demo.port.inbound.dto.OrderRequestDTO;
import com.example.demo.port.inbound.dto.OrderResponseDTO;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
// adapter/web/OrderController.java
@RestController
public class OrderController {
private OrderUseCase orderInputPort;
@PostMapping("/orders")
public OrderResponseDTO placeOrder(@RequestBody OrderRequestDTO request) {
return orderInputPort.placeOrder(request);
}
}
输出端口
package com.example.demo.port.outbound;
import com.example.demo.domain.Order;
public interface OrderRepository {
void save(Order order);
}
输出适配器
package com.example.demo.adapter.outbound.db;
import com.example.demo.domain.Order;
import com.example.demo.port.outbound.OrderRepository;
public class JpaOrderRepository implements OrderRepository {
@Override
public void save(Order order) {
}
}
项目结构

- 如果项目复杂,建议 adapter,port,application,domain 拆成不同的模块
- 建议每一层都有自己的枚举,常量类。 工具类按需,一般适配器层一定需要建工具类目录。
- common :全局工具类,常量
代码示例
常见问题和解决
找不到Port的实现类
可能原因1
start模块没有依赖adapter模块
正确依赖
start->adapter
adapter->application
application->domain,port
port->domain,common
domain->common
可能原因2
AccountMapper 命名为AccountRepositoryImpl,被Spring误认为是Spring要实例化的bean
解决
Springboot启动类增加
@MapperScan("com.kingdee.pe.admin.adapter") // 指定 Mapper 接口所在包
AccountRepository实现修改
@Mapper
public interface AccountMapper extends AccountRepository {
}
可能原因3 Mybatis版本和Springboot版本不一致
可能原因4 Springboot版本和JDK版本不一致

浙公网安备 33010602011771号