mcpField
L
layered-architecture

layered-architecture

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

关于 layered-architecture

为 Spring 应用开发者提供分层架构实践指南,说明控制器、服务、仓库及 DTO 的职责边界。还涵盖映射、配置和横切关注点的组织方式。

主要功能

  1. 01控制器、服务与仓库职责划分
  2. 02请求和响应 DTO 分离
  3. 03独立映射器与静态工厂方法
  4. 04配置层组织规则
  5. 05事务、验证与异常处理规范

适用场景

  1. 01规划 Spring 应用的层级职责
  2. 02避免控制器承载业务逻辑
  3. 03设计实体到响应 DTO 的映射
  4. 04整理事务与异常处理位置

安装

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

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

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

包含的文件

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

agents/
openai.yaml
examples/
bad-service.java
good-service.java
templates/
CreateOrderRequest.java
OrderMapper.java
OrderResponse.java

详细介绍

分层架构

分层规则

@RestController        ← HTTP only. No business logic. No JPA entities in responses.
      ↓ DTOs
@Service               ← All business logic lives here. Orchestrates repositories.
      ↓ Domain objects / Entities
@Repository            ← Data access only. No business logic. Returns entities or projections.
      ↓ JPA / JDBC
Database

控制器层

  • 处理 HTTP:解析请求、验证输入(@Valid)、返回响应
  • 每个端点只调用一个服务方法——不在控制器中编排流程
  • 绝不直接返回 @Entity 类——始终映射为响应 DTO
  • 绝不注入 @Repository——始终通过 @Service 访问
  • 通过 @ControllerAdvice 处理异常,绝不在控制器中使用 try/catch
// ✅ GOOD
@PostMapping("/orders")
public ResponseEntity<OrderResponse> createOrder(@Valid @RequestBody CreateOrderRequest request) {
    Order order = orderService.createOrder(request);
    return ResponseEntity.status(HttpStatus.CREATED).body(OrderResponse.from(order));
}

// ❌ BAD — business logic in controller
@PostMapping("/orders")
public ResponseEntity<Order> createOrder(@RequestBody CreateOrderRequest request) {
    if (request.getItems().isEmpty()) throw new RuntimeException("No items");
    Order order = orderRepository.save(new Order(request)); // direct repo access
    return ResponseEntity.ok(order); // returning entity
}

服务层

  • 包含所有业务逻辑、验证规则和流程编排
  • @Transactional 应放在此处,而非控制器或仓库中
  • 仅使用构造函数注入——绝不使用 @Autowired 字段注入
  • 每个聚合根对应一个服务(使用 OrderService,而不是 OrderAndPaymentService)
  • 返回领域对象或 DTO——绝不返回 HttpServletRequest / HttpServletResponse
// ✅ GOOD
@Service
@RequiredArgsConstructor
public class OrderService {
    private final OrderRepository orderRepository;
    private final InventoryService inventoryService;

    @Transactional
    public Order createOrder(CreateOrderRequest request) {
        inventoryService.reserve(request.items()); // records use accessor names, not getX()
        Order order = Order.from(request);
        return orderRepository.save(order);
    }
}

// ❌ BAD — field injection, HTTP concern in service
@Service
public class OrderService {
    @Autowired private OrderRepository orderRepository;

    public ResponseEntity<Order> createOrder(...) { ... } // HTTP type in service
}

仓库层

  • 继承 JpaRepository<Entity, ID> 或 CrudRepository
  • 通过 @Query 或查询派生定义自定义查询——除非无法避免,否则不使用原始 SQL
  • 返回实体或 Spring Data 投影——绝不返回原始 Object[]
  • 不包含业务逻辑——仅负责数据访问

DTO

  • 分别创建请求 / 响应 DTO——绝不在两者中使用同一个类
  • 仅在请求 DTO 上添加验证注解(@NotNull、@Size 等)
  • 使用静态工厂方法 ResponseDto.from(Entity entity) 进行映射
  • 不可变 DTO 使用 record(Java 16+)
// ✅ GOOD
public record OrderResponse(UUID id, String status, List<LineItemResponse> items, Instant createdAt) {
    public static OrderResponse from(Order order) {
        return new OrderResponse(order.getId(), order.getStatus().name(),
            order.getItems().stream().map(LineItemResponse::from).toList(),
            order.getCreatedAt());
    }
}

映射器模式

  • 不要在控制器和服务中编写映射逻辑——使用专用映射器类或静态工厂方法
  • 映射器应为普通类或工具类——除非需要注入依赖,否则不应设为 Spring Bean
  • 实体 → 响应 DTO:在响应 DTO 上定义静态方法(OrderResponse.from(order))
  • 请求 DTO → 实体:在实体上定义静态工厂方法(Order.from(request)),或使用映射器类
  • 集合映射:使用 .stream().map(OrderResponse::from).toList()——绝不使用手动循环
// ✅ GOOD — dedicated mapper for complex mappings
public class OrderMapper {

    public static OrderResponse toResponse(Order order) {
        return new OrderResponse(
            order.getId(),
            order.getStatus().name(),
            order.getItems().stream().map(OrderMapper::toLineItem).toList(),
            order.getCreatedAt()
        );
    }

    public static Order toEntity(CreateOrderRequest request, User user) {
        Order order = Order.create(request.customerEmail(), user);
        request.items().forEach(item ->
            order.addItem(item.productId(), item.quantity()));
        return order;
    }

    private static LineItemResponse toLineItem(OrderItem item) {
        return new LineItemResponse(item.getProductId(), item.getQuantity(), item.getPrice());
    }
}

配置层

  • @Configuration 类放在 config/ 包中——绝不放在 service/ 或 controller/ 中
  • 配置类绝不导入服务类或控制器类
  • 使用 @ConfigurationProperties 实现类型安全的配置——绝不使用原始 @Value 来配置一组相关设置
  • Bean 定义仅用于基础设施相关事项(RestTemplate、ObjectMapper、SecurityFilterChain)

横切关注点

  • 日志:使用 @Slf4j——绝不使用 System.out.println
  • 验证:在控制器参数上使用 @Valid,自定义验证器使用 @Component
  • 异常处理:使用单个 @RestControllerAdvice 类,绝不在控制器中使用 try/catch
  • 审计:搭配 @EnableJpaAuditing 使用 @CreatedDate / @LastModifiedDate

常见问题

  • Agent 倾向于在控制器上添加 @Transactional——将其移到服务中
  • Agent 使用 @Autowired 字段注入——始终使用构造函数注入(@RequiredArgsConstructor)
  • Agent 从控制器返回 List<Entity>——始终映射为 List<ResponseDto>
  • Agent 创建 OrderAndInventoryService 这样的臃肿类——按聚合拆分
  • Agent 将映射逻辑写在控制器中——提取到映射器类或 DTO 工厂方法中
  • Agent 创建依赖 @Service Bean 的 @Configuration 类——配置类应只负责装配基础设施

评论

支持 Markdown