Skip to content

六边形架构(Hexagonal)和洋葱架构(Onion)是《分层架构》的进化,核心都是依赖倒置——解决"业务依赖数据库"这个老问题。这篇把两者讲清楚:它们怎么把依赖方向反过来、以及它俩的共同点和区别。

一、解决什么问题:业务被数据库绑架

回顾分层架构的问题:

java
@Service
public class OrderService {
    private final OrderMapper orderMapper;   // 业务层直接依赖持久层
    // 业务逻辑里到处是 Mapper,想换数据库、想脱离数据库测试,都难
}

依赖方向是"业务 → 数据库"。业务层被数据库"绑架"了。

六边形和洋葱的思路:把依赖方向反过来——业务核心在中心,数据库、框架、UI 全在外圈,外圈依赖中心,中心绝不依赖外圈

二、核心:依赖倒置 + 端口适配器

关键动作是依赖倒置:业务核心定义"我要什么接口"(端口),外部去实现它(适配器)。

java
// ① 端口(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):业务核心定义的接口(如 OrderRepositoryPaymentGateway
  • 适配器(Adapter):外部的具体实现(MySQL 实现、HTTP 接口、消息队列)

用代码看就清楚了:

java
// 端口(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)

四层用代码看长这样:

java
// ① 中心:领域模型(纯业务对象,不 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 把它系统化成了"四层同心圆 + 依赖规则",讲得更完整、更严格。