hexagonal-architecture-layers-java

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

When to Use

适用场景

Load this skill when:
  • Designing a new Java service with clean, testable layers
  • Refactoring Spring code to isolate the domain from frameworks
  • Supporting multiple adapters (REST + messaging, JPA + Mongo)
  • Enforcing dependency direction and clear module boundaries
在以下场景中使用本技能:
  • 设计具有清晰、可测试分层的全新Java服务
  • 重构Spring代码以将领域层与框架解耦
  • 支持多种适配器(REST + 消息服务、JPA + Mongo)
  • 强制依赖方向与明确的模块边界

Critical Patterns

核心模式

Pattern 1: Domain is pure

模式1:领域层保持纯净

Domain has no framework annotations, no persistence concerns, and no I/O.
领域层不包含框架注解,不处理持久化相关逻辑,也不涉及I/O操作。

Pattern 2: Application orchestrates

模式2:应用层负责编排

Application defines use cases and ports, calling domain logic and delegating I/O to ports.
应用层定义用例与端口,调用领域层逻辑并将I/O操作委托给端口。

Pattern 3: Infrastructure adapts

模式3:基础设施层负责适配

Infrastructure implements ports and wires adapters (controllers, repositories, clients).
基础设施层实现端口,并连接适配器(控制器、仓库、客户端)。

Code Examples

代码示例

Example 1: Domain model + output port

示例1:领域模型 + 输出端口

java
package com.acme.order.domain;

public record OrderId(String value) { }

public final class Order {
  private final OrderId id;
  private final Money total;

  public Order(OrderId id, Money total) {
    this.id = id;
    this.total = total;
  }

  public OrderId id() { return id; }
  public Money total() { return total; }
}
java
package com.acme.order.application.port;

import com.acme.order.domain.Order;
import com.acme.order.domain.OrderId;

public interface OrderRepositoryPort {
  OrderId nextId();
  void save(Order order);
}
java
package com.acme.order.domain;

public record OrderId(String value) { }

public final class Order {
  private final OrderId id;
  private final Money total;

  public Order(OrderId id, Money total) {
    this.id = id;
    this.total = total;
  }

  public OrderId id() { return id; }
  public Money total() { return total; }
}
java
package com.acme.order.application.port;

import com.acme.order.domain.Order;
import com.acme.order.domain.OrderId;

public interface OrderRepositoryPort {
  OrderId nextId();
  void save(Order order);
}

Example 2: Application use case + input port

示例2:应用层用例 + 输入端口

java
package com.acme.order.application.usecase;

import com.acme.order.application.port.OrderRepositoryPort;
import com.acme.order.domain.Order;
import com.acme.order.domain.OrderId;
import com.acme.order.domain.Money;

public interface PlaceOrderUseCase {
  OrderId place(Money total);
}

public final class PlaceOrderService implements PlaceOrderUseCase {
  private final OrderRepositoryPort repository;

  public PlaceOrderService(OrderRepositoryPort repository) {
    this.repository = repository;
  }

  @Override
  public OrderId place(Money total) {
    OrderId id = repository.nextId();
    Order order = new Order(id, total);
    repository.save(order);
    return id;
  }
}
java
package com.acme.order.application.usecase;

import com.acme.order.application.port.OrderRepositoryPort;
import com.acme.order.domain.Order;
import com.acme.order.domain.OrderId;
import com.acme.order.domain.Money;

public interface PlaceOrderUseCase {
  OrderId place(Money total);
}

public final class PlaceOrderService implements PlaceOrderUseCase {
  private final OrderRepositoryPort repository;

  public PlaceOrderService(OrderRepositoryPort repository) {
    this.repository = repository;
  }

  @Override
  public OrderId place(Money total) {
    OrderId id = repository.nextId();
    Order order = new Order(id, total);
    repository.save(order);
    return id;
  }
}

Example 3: Infrastructure adapter + wiring

示例3:基础设施层适配器 + 连接配置

java
package com.acme.order.infrastructure.persistence;

import com.acme.order.application.port.OrderRepositoryPort;
import com.acme.order.domain.Order;
import com.acme.order.domain.OrderId;
import org.springframework.stereotype.Repository;

@Repository
public final class OrderJpaAdapter implements OrderRepositoryPort {
  private final SpringOrderRepository repository;
  private final OrderMapper mapper;

  public OrderJpaAdapter(SpringOrderRepository repository, OrderMapper mapper) {
    this.repository = repository;
    this.mapper = mapper;
  }

  @Override
  public OrderId nextId() {
    return new OrderId(java.util.UUID.randomUUID().toString());
  }

  @Override
  public void save(Order order) {
    repository.save(mapper.toEntity(order));
  }
}
java
package com.acme.order.infrastructure.persistence;

import com.acme.order.application.port.OrderRepositoryPort;
import com.acme.order.domain.Order;
import com.acme.order.domain.OrderId;
import org.springframework.stereotype.Repository;

@Repository
public final class OrderJpaAdapter implements OrderRepositoryPort {
  private final SpringOrderRepository repository;
  private final OrderMapper mapper;

  public OrderJpaAdapter(SpringOrderRepository repository, OrderMapper mapper) {
    this.repository = repository;
    this.mapper = mapper;
  }

  @Override
  public OrderId nextId() {
    return new OrderId(java.util.UUID.randomUUID().toString());
  }

  @Override
  public void save(Order order) {
    repository.save(mapper.toEntity(order));
  }
}

Anti-Patterns

反模式

Don't: Put framework annotations in domain

不要:在领域层中添加框架注解

java
// BAD: domain tied to JPA
@jakarta.persistence.Entity
public class Order {
  @jakarta.persistence.Id
  private String id;
}
java
// 错误示例:领域层与JPA绑定
@jakarta.persistence.Entity
public class Order {
  @jakarta.persistence.Id
  private String id;
}

Don't: Call infrastructure directly from domain

不要:从领域层直接调用基础设施层

java
// BAD: domain depends on Spring repository
public class Order {
  private final SpringOrderRepository repository;
}
java
// 错误示例:领域层依赖Spring仓库
public class Order {
  private final SpringOrderRepository repository;
}

Quick Reference

速查表

TaskPattern
Persist domain dataDefine output port in application, implement in infrastructure
Expose use caseDefine input port and service in application
Keep domain pureNo annotations, no I/O, no framework imports
任务模式
持久化领域数据在应用层定义输出端口,在基础设施层实现
暴露用例在应用层定义输入端口与服务
保持领域层纯净无注解、无I/O、无框架导入

Resources

参考资源