六边形架构 Hexagonal Architecture

FreeGuideOnline 最新 2026-07-12

什么是六边形架构

六边形架构(Hexagonal Architecture),又称端口与适配器架构(Ports and Adapters),是由 Alistair Cockburn 在 2005 年提出的一种软件设计模式。它旨在将应用程序的核心业务逻辑与外部世界(如数据库、用户界面、第三方服务)完全隔离,使系统更易于测试、维护和演化。

其核心思想是:将应用程序视为一个被端口包围的六边形,外部请求只能通过这些端口进入内部,内部的业务逻辑也仅通过端口与外界交互。六边形的每条边代表一种交互通道,而适配器负责将具体的外部技术连接到对应的端口上。

核心概念:端口与适配器

端口(Port)

端口是应用程序定义的抽象接口,描述了一种与外界交互的契约。端口不关心具体的实现技术,它们只说明“我需要什么”或者“我能提供什么”。

端口分为两类:

  • 驱动端(Driving Side)端口:由外界主动发起调用,用以触发应用程序的业务逻辑。例如:HTTP API 接口、命令行接口、消息队列监听器等。对应的端口抽象通常是应用服务接口或命令处理器接口。
  • 被驱动端(Driven Side)端口:由应用程序主动调用,用以获取外部资源或发送数据。例如:数据库仓储接口、邮件发送接口、事件总线接口等。

适配器(Adapter)

适配器是端口的具体实现,负责将外部技术的细节适配为端口所要求的格式。适配器位于六边形之外,可以被随时替换而不影响核心逻辑。

  • 驱动适配器:例如一个 REST 控制器,它接收 HTTP 请求,将其转换为调用应用服务的参数,并将返回结果格式化为 JSON 响应。
  • 被驱动适配器:例如一个 MySQL 数据库实现,它具体执行 SQL 语句,并把查询结果转换为领域对象返回给核心层。

架构示意

虽然无法用图片直接展示,但可以用文字描述典型的六边形架构分层:

[ 外部世界 ]  <——>  [ 适配器层 ]  <——>  [ 端口接口 ]  <——>  [ 应用核心 ]
   (Web/DB/CLI)       (REST/MySQL/…)       (接口定义)       (领域+应用服务)

应用核心完全不知道外部世界的存在,所有依赖都由外向内指向核心。这种依赖方向保证了核心的独立性和可测试性。

与传统分层架构的对比

传统分层架构 六边形架构
上层依赖下层,依赖方向固定 依赖倒置,所有依赖指向核心
业务逻辑容易与技术细节耦合 技术与业务完全解耦
更换数据库或 UI 需要改动业务层 只需替换适配器,核心不变
测试通常需要启动整个栈 核心可以完全隔离测试

在传统三层架构(Controller → Service → Repository)中,Service 往往会直接依赖 Repository 的具体实现(如 JPA Repository),导致业务逻辑被框架绑架。而六边形架构强制在 Service 层只依赖接口,由适配器实现该接口并注入。

核心优势

  1. 技术无关性:可以推迟技术选型,先专注于业务建模。对于新技术,只需编写新的适配器。
  2. 高可测试性:核心领域逻辑可以在不依赖数据库、网络等任何外部资源的情况下进行单元测试。
  3. 可替换性:更换数据库、消息队列或前端框架时,仅需更换适配器,核心代码零改动。
  4. 关注点分离:业务规则清晰集中,不会夹杂技术细节,代码更易理解。
  5. 允许演进:随着需求变化,可以逐步添加新的端口和适配器,而不会影响现有功能。

实战要点与基本原则

1. 核心只依赖抽象,不依赖具体实现

编写领域服务时,只使用接口来定义所需的外部依赖(如 OrderRepository 接口),绝不使用具体的类(如 JpaOrderRepository)。

2. 适配器负责协议转换

适配器必须将外部协议格式的数据转换为核心需要的领域对象,以及将领域对象转换为外部所需的格式。例如,REST 适配器将 JSON 请求转为领域命令对象。

3. 按端口组织测试

  • 驱动端测试:通过端口调用核心,验证业务规则。通常使用 mock 的被驱动适配器。
  • 被驱动端测试:针对真实的外部技术编写集成测试,确保适配器符合端口契约。

4. 项目的典型包结构建议

domain/          ← 核心领域模型、领域服务、端口接口
application/     ← 应用服务、用例编排,依赖领域端口
adapters/
  drivings/      ← REST控制器、CLI命令、消息监听器等
  driven/        ← 数据库实现、外部API客户端、邮件发送器等
configuration/   ← 依赖注入、适配器装配

如何开始实践

步骤 1:识别核心领域

抽取与具体技术无关的业务实体、值对象和领域服务。例如在电商系统中,提取 OrderProductPricingService 等概念。

步骤 2:定义端口接口

根据用例定义驱动端口(应用服务接口)和被驱动端口(仓储接口、通知接口等)。例如:

// 被驱动端口:订单仓储
public interface OrderRepository {
    void save(Order order);
    Optional<Order> findById(OrderId id);
}

// 驱动端口:下单用例接口
public interface PlaceOrderUseCase {
    OrderConfirmation execute(PlaceOrderCommand command);
}

步骤 3:实现核心业务

核心领域服务只依赖端口接口。例如 PlaceOrderService 通过构造函数注入 OrderRepository 接口,通过调用仓储来持久化。

步骤 4:构建适配器

  • OrderRepository 编写一个基于 JPA 的实现 JpaOrderRepository
  • PlaceOrderUseCase 创建一个 REST 控制器 OrderController,它调用用例并将结果返回。

步骤 5:组装应用

在启动模块中使用依赖注入将所有组件连接起来,将适配器的实例注入到核心需要的端口。

测试策略

六边形架构为测试带来了极大的便利:

  • 快速领域测试:对核心业务逻辑编写单元测试,不需要任何框架或外部资源,使用模拟的端口实现即可。例如在测试 PlaceOrderService 时,注入一个内存实现的 OrderRepository
  • 适配器集成测试:单独测试每个适配器是否正确地实现了端口契约。例如使用测试容器测试 JpaOrderRepository,确保其真的能够保存和读取数据。
  • 全栈端到端测试:按需编写少量测试覆盖关键场景,验证所有适配器连接正确。

常见误区与注意事项

  • 过度设计:不要为了六边形而六边形。在简单的 CRUD 应用或原型中,可能不需要如此严格的隔离。应从业务复杂度出发。
  • 端口不是必须六个:六边形的边数可以灵活,一般只有 2 - 4 个端口,驱动力来自于“隔离”的需求而非形状。
  • 避免端口泄漏:不要将技术细节带入端口接口。例如不要在仓储接口中抛框架特定的异常,或使用框架独有的分页对象。
  • 不要忽略应用服务层:端口的调用和编排通常需要一个应用服务层,它负责事务、权限检查、事件发布等,领域服务应保持纯净。

总结

六边形架构通过“端口与适配器”的模式,将核心业务逻辑完全隔离在应用中心,使得软件能够从容应对技术环境的变化。它强调依赖倒置和接口隔离,带来了卓越的可测试性、可维护性和技术灵活性。对于中大型业务系统或需要长期演进的项目,采用六边形架构能够显著降低技术债务,提高团队开发效率。入门时建议从一个明确的边界上下文开始,逐步体会核心与外部解耦的价值。