在传统分层架构(Controller → Service → DAO)中,业务代码常常被接口、第三方调用、数据库、消息中间件等外部技术细节裹挟。一旦数据源、对接渠道、外部系统发生变更,核心业务就要大面积改代码,测试困难、迭代风险高。
什么是六边形架构?六边形架构(Hexagonal Architecture)也叫端口与适配器架构,是 DDD 领域驱动设计最经典的落地架构之一。
本文实现可落地、可用于生产的六边形架构,以订单创建场景作为案例,语言通俗、代码完整,适配 Java/SpringBoot 主流技术栈。
一、实战场景说明
实现一个极简但完整的业务:用户创建订单 → 校验订单 → 保存订单 → 发送通知
严格遵守六边形架构规则:
- 领域层完全纯净,不依赖任何框架、DB、第三方接口
- 依赖方向永远向内,外层依赖内层
- 端口 = 接口,适配器 = 实现,业务与技术彻底隔离
二、项目包结构(六边形标准结构)
Plain Text com.order ├── domain/ # 内核:领域层(纯业务,无任何外部依赖) │ ├── entity/ # 领域实体 │ └── service/ # 领域服务(核心业务规则) ├── application/ # 内核:应用层(流程编排) │ └── service/ # 应用服务 ├── port/ # 端口:抽象接口(业务定义能力) │ ├── inbound/ # 入站端口:外部调用系统 │ └── outbound/ # 出站端口:系统调用外部 ├── adapter/ # 适配器:具体实现(技术细节) │ ├── inbound/ # 入站适配器:Controller、RPC、Job │ └── outbound/ # 出站适配器:DB、Redis、第三方接口 └── OrderApplication.java # 启动类 |
一句话看懂:
domain + application = 系统心脏
port = 插座(抽象)
adapter = 插头(实现)
三、代码实现(完整可复制)
1. 领域层(最内层,纯业务,零依赖)
1.1 领域实体:Order.java
java package com.order.domain.entity;
import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime;
/** * 领域实体:只包含业务属性与业务行为 * 无任何框架注解、无DB注解、无第三方依赖 */ @Data public class Order { private String orderId; private String userId; private BigDecimal amount; private LocalDateTime createTime;
// 领域行为:订单创建规则 public void create() { if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) { throw new RuntimeException("订单金额必须大于0"); } this.createTime = LocalDateTime.now(); } } |
1.2 领域服务:OrderDomainService.java
java package com.order.domain.service;
import com.order.domain.entity.Order; import org.springframework.stereotype.Service;
/** * 领域服务:处理跨实体的核心业务规则 * 纯业务逻辑,不处理技术、不操作DB */ @Service public class OrderDomainService {
public void createOrder(Order order) { // 执行业务行为 order.create(); // 可继续添加:库存校验、价格计算、风控规则等纯业务逻辑 } } |
2. 端口层(抽象接口:定义能力,不做实现)
2.1 入站端口(给外部调用)
java package com.order.port.inbound;
import com.order.domain.entity.Order;
public interface CreateOrderUseCase { Order createOrder(Order order); } |
2.2 出站端口(定义需要外部能力:DB、消息等)
java package com.order.port.outbound;
import com.order.domain.entity.Order;
// 订单仓储端口(抽象) public interface OrderRepositoryPort { Order save(Order order); } |
java package com.order.port.outbound;
// 通知端口(抽象) public interface NotifyPort { void sendNotify(String orderId); } |
3. 应用层(流程编排,不写业务规则)
java package com.order.application.service;
import com.order.domain.entity.Order; import com.order.domain.service.OrderDomainService; import com.order.port.inbound.CreateOrderUseCase; import com.order.port.outbound.NotifyPort; import com.order.port.outbound.OrderRepositoryPort; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;
/** * 应用服务:只做流程编排,不写核心业务规则 * 依赖:领域服务 + 出站端口(抽象接口) */ @Service @RequiredArgsConstructor public class OrderApplicationService implements CreateOrderUseCase {
private final OrderDomainService orderDomainService; private final OrderRepositoryPort orderRepositoryPort; private final NotifyPort notifyPort;
@Override @Transactional(rollbackFor = Exception.class) public Order createOrder(Order order) { // 1. 领域层执行业务规则 orderDomainService.createOrder(order);
// 2. 调用出站端口(保存订单,具体实现由适配器完成) Order savedOrder = orderRepositoryPort.save(order);
// 3. 调用出站端口(发送通知) notifyPort.sendNotify(savedOrder.getOrderId());
return savedOrder; } } |
4. 适配器层(技术实现,全部放在外层)
4.1 入站适配器:HTTP 接口(Controller)
java package com.order.adapter.inbound;
import com.order.domain.entity.Order; import com.order.port.inbound.CreateOrderUseCase; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController @RequestMapping("/order") @RequiredArgsConstructor public classOrderController {
private final CreateOrderUseCase createOrderUseCase;
@PostMapping("/create") public ResponseEntity<Order> createOrder(@RequestBody Order order) { return ResponseEntity.ok(createOrderUseCase.createOrder(order)); } } |
4.2 出站适配器:DB 实现(MySQL)
java package com.order.adapter.outbound;
import com.order.domain.entity.Order; import com.order.port.outbound.OrderRepositoryPort; import com.order.infra.mapper.OrderMapper; import com.order.infra.po.OrderPO; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component;
/** * DB 适配器:实现端口接口 * 领域层完全不知道DB是什么 */ @Component @RequiredArgsConstructor public class OrderRepositoryAdapter implements OrderRepositoryPort {
private final OrderMapper orderMapper;
@Override public Order save(Order order) { // 领域实体 → 持久化对象 OrderPO orderPO = convert(order); orderMapper.insert(orderPO); return order; }
private OrderPO convert(Order order) { // 省略转换逻辑 OrderPO po = new OrderPO(); po.setOrderId(order.getOrderId()); po.setUserId(order.getUserId()); po.setAmount(order.getAmount()); return po; } } |
4.3 出站适配器:通知实现
java package com.order.adapter.outbound;
import com.order.port.outbound.NotifyPort; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component;
/** * 通知适配器:可随时替换为短信、邮件、MQ * 领域/应用层完全无感 */ @Component @RequiredArgsConstructor public class NotifyAdapter implements NotifyPort { @Override public void sendNotify(String orderId) { System.out.println("【适配器】发送订单通知:" + orderId); } } |
四、核心亮点:六边形架构威力体现
场景 1:切换数据库
你只需要新增一个适配器:
OrderRepositoryMongoAdapter
领域层、应用层一行代码都不用改!
场景 2:切换通知方式
短信 → 微信 → MQ
只需要修改NotifyAdapter
业务内核完全不动
场景 3:单元测试极简单
java @MockBean private OrderRepositoryPort orderRepositoryPort;
@MockBean private NotifyPort notifyPort; |
不用启动 DB、MQ、Redis,直接测试业务逻辑。
五、架构合规性检查
✅ 领域层:无任何外部依赖
✅ 应用层:只依赖领域与端口
✅ 适配器:全部在最外层
✅ 依赖方向:永远向内
✅ 业务与技术完全解耦
这就是标准六边形架构 + DDD 落地形态。
六、总结
六边形架构不是炫技,它真正解决的是:
业务代码不被技术污染,系统永远可扩展、可替换、可测试。
在实战中只要记住三句话:
- 领域层写业务,不碰任何技术
- 端口是抽象,适配器是实现
- 所有依赖必须向内,绝不外翻
这套代码可以直接用于电商、支付、SAAS、微服务项目。