关于 hexagonal-architecture
面向采用 Spring 的 Java 项目,提供六边形架构的分层与代码组织指导。它强调纯 Java 领域层、端口接口,以及由应用层和基础设施适配器承担编排与实现。
主要功能
- 01领域、应用与基础设施分层
- 02无框架依赖的领域模型
- 03区分驱动端口与被驱动端口
- 04由适配器实现领域端口
- 05常见架构错误提示
适用场景
- 01规划 Java 项目的包结构
- 02将持久化实现与用例隔离
- 03检查领域逻辑和 Spring 注解的放置位置
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ rrezartprebreza/ spring-boot-skills --skill hexagonal-architecture安装后在 Claude Code 中输入/hexagonal-architecture确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlexamples/bad-use-case.javagood-use-case.javatemplates/OrderJpaAdapter.javaOrderJpaEntity.java详细介绍
六边形架构
包结构
src/main/java/com/example/
├── domain/ ← Pure Java. Zero framework dependencies.
│ ├── model/ ← Entities, value objects, aggregates
│ ├── port/
│ │ ├── in/ ← Use case interfaces (driving ports)
│ │ └── out/ ← Repository/external interfaces (driven ports)
│ └── service/ ← Domain services (pure business logic)
├── application/ ← Orchestrates use cases. Spring allowed here.
│ └── usecase/ ← @Service implementations of domain ports
└── infrastructure/ ← All framework/DB/HTTP details
├── persistence/ ← JPA adapters implementing out ports
├── web/ ← REST controllers (driving adapters)
└── external/ ← HTTP clients, messaging adapters
领域层 — 零 Spring
// domain/model/Order.java — pure Java, no annotations
public class Order {
private final OrderId id;
private final CustomerId customerId;
private OrderStatus status;
private final List<OrderItem> items;
private Order(OrderId id, CustomerId customerId) {
this.id = id;
this.customerId = customerId;
this.status = OrderStatus.PENDING;
this.items = new ArrayList<>();
}
public static Order create(CustomerId customerId) {
return new Order(OrderId.generate(), customerId);
}
public void addItem(ProductId productId, int quantity, Money price) {
if (status != OrderStatus.PENDING)
throw new OrderNotModifiableException(id);
items.add(new OrderItem(productId, quantity, price));
}
// Getters only — no setters
}
// domain/model/OrderId.java — value object
public record OrderId(UUID value) {
public static OrderId generate() { return new OrderId(UUID.randomUUID()); }
public static OrderId of(String value) { return new OrderId(UUID.fromString(value)); }
}
端口 — 仅接口
// domain/port/in/CreateOrderUseCase.java — driving port
public interface CreateOrderUseCase {
Order createOrder(CreateOrderCommand command);
}
// domain/port/in/CreateOrderCommand.java
public record CreateOrderCommand(CustomerId customerId, List<OrderItemData> items) {}
// domain/port/out/OrderRepository.java — driven port
public interface OrderRepository {
Order save(Order order);
Optional<Order> findById(OrderId id);
void deleteById(OrderId id);
}
// domain/port/out/InventoryPort.java — driven port
public interface InventoryPort {
void reserve(List<OrderItem> items);
void release(List<OrderItem> items);
}
应用层 — 用例实现
// application/usecase/CreateOrderService.java
@Service // Spring allowed here
@RequiredArgsConstructor
@Transactional
public class CreateOrderService implements CreateOrderUseCase {
private final OrderRepository orderRepository; // domain port (not JPA repo)
private final InventoryPort inventoryPort; // domain port
@Override
public Order createOrder(CreateOrderCommand command) {
Order order = Order.create(command.customerId());
command.items().forEach(item ->
order.addItem(item.productId(), item.quantity(), item.price()));
inventoryPort.reserve(order.getItems());
return orderRepository.save(order);
}
}
基础设施 — 适配器
// infrastructure/persistence/JpaOrderRepository.java — implements domain port
@Repository
@RequiredArgsConstructor
public class JpaOrderRepository implements OrderRepository {
private final SpringDataOrderRepository springDataRepo;
private final OrderMapper mapper;
@Override
public Order save(Order order) {
OrderJpaEntity entity = mapper.toEntity(order);
return mapper.toDomain(springDataRepo.save(entity));
}
@Override
public Optional<Order> findById(OrderId id) {
return springDataRepo.findById(id.value()).map(mapper::toDomain);
}
@Override
public void deleteById(OrderId id) {
springDataRepo.deleteById(id.value());
}
}
// Separate Spring Data interface — infrastructure detail
interface SpringDataOrderRepository extends JpaRepository<OrderJpaEntity, UUID> {}
// infrastructure/web/OrderController.java — driving adapter
@RestController
@RequestMapping("/api/v1/orders")
@RequiredArgsConstructor
public class OrderController {
private final CreateOrderUseCase createOrderUseCase; // injects use case port
@PostMapping
public ResponseEntity<ApiResponse<OrderResponse>> create(@Valid @RequestBody CreateOrderRequest request) {
Order order = createOrderUseCase.createOrder(request.toCommand());
return ResponseEntity.status(201).body(ApiResponse.ok(OrderResponse.from(order)));
}
}
注意事项
- Agent 在领域类中导入
javax.persistence— 领域层必须与框架无关 - Agent 将
JpaRepository直接注入用例 — 应使用领域端口接口 - Agent 在领域服务上添加
@Transactional— 该注解应放在应用层 - Agent 混淆驱动端口和被驱动端口 —
port/in= 应用提供的内容,port/out= 应用需要的内容 - Agent 创建只有 getter/setter 的贫血领域模型 — 行为应属于领域对象
评论
支持 Markdown