#六边形架构
#一个订单系统的重构
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 | 有实际落地经验,能设计依赖注入配置 |