六边形架构

一个订单系统的重构

2019年,我们团队的订单系统变得越来越难维护:

@Service
class OrderService {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private KafkaTemplate kafkaTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    // 1000 行代码,直接依赖了 4 个外部框架
}

问题是:每换一个技术栈(比如从 Kafka 换成 RocketMQ),订单 Service 都要改。

六边形架构解决的就是这个问题:让核心业务逻辑完全不知道外部世界的存在。


二、六边形架构核心🔴

2.1 核心思想

六边形架构把系统分成三部分:

        ┌───────────────────────────────┐
        │           端口(Ports)        │
        │  ┌─────────────────────────┐  │
        │  │   核心业务(Application)  │  │
        │  │                         │  │
        │  │                         │  │
        │  └─────────────────────────┘  │
        └───────────────────────────────┘

核心业务:完全独立,不依赖任何外部框架
端口:定义与外部世界交互的接口
适配器:实现端口,连接外部框架

2.2 端口(Ports)

// 输入端口(Driving Port):定义可以被外部调用的操作
interface CreateOrderPort {
    Order createOrder(CreateOrderCommand command);
}

// 输出端口(Driven Port):定义核心业务需要调用的外部能力
interface OrderRepository {
    void save(Order order);
    Optional<Order> findById(OrderId id);
}

interface PaymentGateway {
    PaymentResult pay(Money amount, String channel);
}

interface EventPublisher {
    void publish(DomainEvent event);
}

2.3 适配器(Adapters)

// 输入适配器:HTTP → 端口
@RestController
class OrderController implements CreateOrderPort {
    private final CreateOrderUseCase useCase;

    @PostMapping("/orders")
    public Order createOrder(@RequestBody CreateOrderRequest request) {
        return useCase.execute(toCommand(request));
    }
}

// 输出适配器:MySQL → 端口
class JpaOrderRepository implements OrderRepository {
    @Override
    public void save(Order order) {
        // JPA 实现
    }
}

// 输出适配器:Kafka → 端口
class KafkaEventPublisher implements EventPublisher {
    @Override
    public void publish(DomainEvent event) {
        kafkaTemplate.send("events", event);
    }
}

2.4 核心业务完全不依赖外部

// 核心业务:只依赖端口接口
class CreateOrderUseCase implements CreateOrderPort {
    private final OrderRepository orderRepository;
    private final EventPublisher eventPublisher;

    // 构造函数注入,不依赖具体实现
    public CreateOrderUseCase(
            OrderRepository orderRepository,
            EventPublisher eventPublisher) {
        this.orderRepository = orderRepository;
        this.eventPublisher = eventPublisher;
    }

    @Override
    public Order createOrder(CreateOrderCommand command) {
        Order order = new Order(command.getUserId(), command.getItems());

        // 核心业务逻辑
        order.validate();
        order.calculateTotal();

        // 通过端口调用外部
        orderRepository.save(order);
        eventPublisher.publish(new OrderCreatedEvent(order));

        return order;
    }
}

三、六边形 vs 分层架构🔴

维度分层架构六边形架构
核心业务位置中间层最中心
依赖方向上层依赖下层核心不依赖外部
测试性需要 mock核心可独立测试
可替换性中等
学习曲线

四、Spring Boot 落地🟡

4.1 目录结构

src/main/java/com/example
├── domain                    # 核心领域(完全独立)
│   ├── model
│   │   ├── Order.java
│   │   └── Money.java
│   ├── port
│   │   ├── in
│   │   │   └── CreateOrderPort.java
│   │   └── out
│   │       ├── OrderRepository.java
│   │       ├── PaymentGateway.java
│   │       └── EventPublisher.java
│   └── service
│       └── CreateOrderUseCase.java

├── adapter                   # 适配器
│   ├── inbound
│   │   └── http
│   │       └── OrderController.java
│   └── outbound
│       ├── persistence
│       │   └── JpaOrderRepository.java
│       ├── payment
│       │   └── AlipayAdapter.java
│       └── messaging
│           └── KafkaEventPublisher.java

└── config
    └── DependencyInjectionConfig.java

4.2 依赖注入配置

@Configuration
class DependencyInjectionConfig {

    // 核心用例
    @Bean
    public CreateOrderUseCase createOrderUseCase(
            OrderRepository orderRepository,
            EventPublisher eventPublisher) {
        return new CreateOrderUseCase(orderRepository, eventPublisher);
    }

    // 输出适配器
    @Bean
    public OrderRepository orderRepository(JpaRepositoryFactory factory) {
        return new JpaOrderRepository(factory);
    }

    @Bean
    public EventPublisher eventPublisher(KafkaTemplate kafkaTemplate) {
        return new KafkaEventPublisher(kafkaTemplate);
    }

    // 输入适配器
    @Bean
    public OrderController orderController(CreateOrderUseCase useCase) {
        return new OrderController(useCase);
    }
}

【架构权衡】 六边形架构的核心价值是让核心业务完全可测试和可替换。如果你的核心业务需要长期维护、会被多个客户端调用、可能更换技术栈,那么六边形架构值得投入。如果只是简单 CRUD,六边形架构反而增加复杂度。


五、面试总结

级别期望回答
P5能说出端口和适配器的概念
P6能对比六边形和分层架构
P7有实际落地经验,能设计依赖注入配置