本文へ移動
cccskills
無料GitHub で公開

layered-architecture

Use when generating or modifying any Spring Boot class — controllers, services, repositories, DTOs, mappers, or configuration. Enforces strict layer separation and prevents business logic from leaking across boundaries.

インストール方法を見る

含まれるファイル(6)

  • SKILL.md6.1 KB
  • examples/bad-service.java1.5 KB
  • examples/good-service.java1.2 KB
  • templates/CreateOrderRequest.java795 B
  • templates/OrderMapper.java1.0 KB
  • templates/OrderResponse.java911 B

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Layered Architecture

Layer Rules

@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

Controller Layer

  • Handles HTTP: parsing requests, validating input (@Valid), returning responses
  • Calls ONE service method per endpoint — no orchestration in controllers
  • Never returns @Entity classes directly — always map to response DTOs
  • Never injects @Repository — always goes through a @Service
  • Exception handling via @ControllerAdvice, never try/catch in controllers
// ✅ 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
}

Service Layer

  • Contains all business logic, validation rules, and orchestration
  • @Transactional lives here, not in controllers or repositories
  • Constructor injection only — never @Autowired field injection
  • One service per aggregate root (OrderService, not OrderAndPaymentService)
  • Returns domain objects or DTOs — never 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.getItems());
        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
}

Repository Layer

  • Extends JpaRepository<Entity, ID> or CrudRepository
  • Custom queries via @Query or query derivation — no raw SQL unless unavoidable
  • Returns entities or Spring Data Projections — never raw Object[]
  • No business logic — pure data access

DTOs

  • Separate Request / Response DTOs — never use the same class for both
  • Validation annotations (@NotNull, @Size, etc.) on Request DTOs only
  • Static factory method ResponseDto.from(Entity entity) for mapping
  • Use records for immutable DTOs (Java 16+)
// ✅ GOOD
public record OrderResponse(UUID id, String status, List<LineItemResponse> items) {
    public static OrderResponse from(Order order) {
        return new OrderResponse(order.getId(), order.getStatus().name(),
            order.getItems().stream().map(LineItemResponse::from).toList());
    }
}

Mapper Pattern

  • Keep mapping logic out of controllers and services — use dedicated mapper classes or static factory methods
  • Mapper is a plain class or utility — not a Spring bean unless it needs injected dependencies
  • Entity → Response DTO: static method on the response DTO (OrderResponse.from(order))
  • Request DTO → Entity: static factory on the entity (Order.from(request)) or a mapper class
  • Collection mapping: use .stream().map(OrderResponse::from).toList() — never manual loops
// ✅ 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 Layer

  • @Configuration classes live in a config/ package — never in service/ or controller/
  • Configuration never imports service or controller classes
  • Use @ConfigurationProperties for type-safe config — never raw @Value for groups of related settings
  • Bean definitions for infrastructure concerns only (RestTemplate, ObjectMapper, SecurityFilterChain)

Cross-Cutting Concerns

  • Logging: use @Slf4j — never System.out.println
  • Validation: @Valid on controller parameters, custom validators as @Component
  • Exception handling: single @RestControllerAdvice class, never try/catch in controllers
  • Auditing: @CreatedDate / @LastModifiedDate with @EnableJpaAuditing

Gotchas

  • Agent tends to put @Transactional on controllers — move it to services
  • Agent uses @Autowired field injection — always use constructor injection (@RequiredArgsConstructor)
  • Agent returns List<Entity> from controllers — always map to List<ResponseDto>
  • Agent creates OrderAndInventoryService god classes — split by aggregate
  • Agent puts mapping logic inside controllers — extract to mapper class or DTO factory method
  • Agent creates @Configuration classes that depend on @Service beans — configuration should only wire infrastructure

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Use when adding monitoring, metrics, logging, or tracing to Spring AI or LLM integration code. Covers token tracking, latency measurement, cost estimation, and prompt/response logging. Use when user mentions AI monitoring, token costs, or LLM observability.

日本語の概要は準備中です。原文の説明を表示しています。

lgzarturo/springboot-course32026年10月12日 更新

android

無料

Provides expert knowledge for building modern native Android apps using Kotlin, Jetpack Compose, Material Design 3, MVI architecture, Hilt dependency injection, and Media3/ExoPlayer.

日本語の概要は準備中です。原文の説明を表示しています。

lgzarturo/springboot-course32026年10月12日 更新

Design, implement, review, and audit Android Jetpack Compose UI/UX for phone interfaces. Use when a request concerns concrete Compose layout, components, interaction, accessibility, or visual behavior. Do not activate for framework or dependency presence alone; non-UI Android work is excluded.

日本語の概要は準備中です。原文の説明を表示しています。

lgzarturo/springboot-course32026年10月12日 更新

Provides expert knowledge for designing, implementing, and managing REST API versioning strategies with deprecation workflows.

日本語の概要は準備中です。原文の説明を表示しています。

lgzarturo/springboot-course32026年10月12日 更新

astro

無料

Provides expert knowledge for building Astro 5+ sites with Islands Architecture, Content Collections, TypeScript, and performance-first rendering strategies.

日本語の概要は準備中です。原文の説明を表示しています。

lgzarturo/springboot-course32026年10月12日 更新

Audits token storage mechanisms to prevent XSS-based JWT theft.

日本語の概要は準備中です。原文の説明を表示しています。

lgzarturo/springboot-course32026年10月12日 更新

lgzarturo のスキルをすべて見る

このスキルの問題を報告する