mcpField
H
hexagonal-architecture

hexagonal-architecture

github.com/rrezartprebreza开发345107 次安装2 天前更新
代码仓库

关于 hexagonal-architecture

面向采用 Spring 的 Java 项目,提供六边形架构的分层与代码组织指导。它强调纯 Java 领域层、端口接口,以及由应用层和基础设施适配器承担编排与实现。

主要功能

  1. 01领域、应用与基础设施分层
  2. 02无框架依赖的领域模型
  3. 03区分驱动端口与被驱动端口
  4. 04由适配器实现领域端口
  5. 05常见架构错误提示

适用场景

  1. 01规划 Java 项目的包结构
  2. 02将持久化实现与用例隔离
  3. 03检查领域逻辑和 Spring 注解的放置位置

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/rrezartprebreza/spring-boot-skills --skill hexagonal-architecture

安装后在 Claude Code 中输入/hexagonal-architecture确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

agents/
openai.yaml
examples/
bad-use-case.java
good-use-case.java
templates/
OrderJpaAdapter.java
OrderJpaEntity.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