본문으로 건너뛰기
cccskills
무료GitHub에 공개

spring-conventions

Spring Boot coding conventions: REST controller design, service layer, dependency injection, configuration, DTO patterns, exception handling, and Bean Validation. Apply on any Spring Boot project. Works alongside spring-boot-plugin:spring-data-jpa for persistence and java-foundation:java-conventions for domain objects. Use this skill to: - Structure REST endpoints with @RestController, proper HTTP semantics, and DTO mapping. - Enforce constructor injection and @ConfigurationProperties over @Value on fields. - Write @Service classes with transactional boundaries at the right layer. - Handle errors consistently with @RestControllerAdvice and ProblemDetail (RFC 9457). Do NOT use this skill for: - JPA entity and repository patterns — see spring-boot-plugin:spring-data-jpa. - Spring Security configuration — see the security phase injection in stack.md. - Build tool configuration — see java-foundation:build-tooling.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(1)

  • SKILL.md8.3 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Spring Boot Conventions

REST Controller design

@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor            // Lombok — generates constructor for final fields
public class UserController {

    private final UserService userService;

    @GetMapping("/{id}")
    public UserResponse getById(@PathVariable Long id) {
        return userService.findById(id)
            .orElseThrow(() -> new EntityNotFoundException("User", id));
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public UserResponse create(@RequestBody @Valid CreateUserRequest request) {
        return userService.create(request);
    }

    @PutMapping("/{id}")
    public UserResponse update(@PathVariable Long id,
                               @RequestBody @Valid UpdateUserRequest request) {
        return userService.update(id, request);
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void delete(@PathVariable Long id) {
        userService.delete(id);
    }
}

Return DTO directly (not ResponseEntity<T>) unless you need explicit control over headers or status codes beyond @ResponseStatus. ResponseEntity is valid for partial-update responses (202 Accepted + Location header) or conditional-GET (ETag / If-None-Match).

HTTP semantics:

  • GET — idempotent, no body.
  • POST — create, return 201 Created + new resource.
  • PUT — replace entire resource, return 200 OK with updated resource.
  • PATCH — partial update.
  • DELETE — return 204 No Content.

DTO patterns

// Request DTO — mutable, with Bean Validation
public record CreateUserRequest(
    @NotBlank @Email String email,
    @NotBlank @Size(min = 8) String password,
    @NotNull Role role
) {}

// Response DTO — immutable record, projection of domain data
public record UserResponse(
    Long id,
    String email,
    Role role,
    Instant createdAt
) {
    public static UserResponse from(User user) {
        return new UserResponse(user.id(), user.getEmail(), user.getRole(), user.getCreatedAt());
    }
}

Separate request and response types. Never expose the JPA entity directly from the controller — it leaks schema details and creates bidirectional serialization issues.

Use static factory methods (from(Entity)) on response records for clean mapping without a mapper library when the mapping is trivial. For complex mappings with many fields, introduce a UserMapper interface and implement with MapStruct or plain Java.

Service layer

@Service
@Transactional(readOnly = true)     // Default to read-only; override on write methods
@RequiredArgsConstructor
public class UserService {

    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;

    public Optional<UserResponse> findById(Long id) {
        return userRepository.findById(id).map(UserResponse::from);
    }

    @Transactional
    public UserResponse create(CreateUserRequest request) {
        if (userRepository.existsByEmail(request.email())) {
            throw new DuplicateEmailException(request.email());
        }
        var user = new User(request.email(), passwordEncoder.encode(request.password()), request.role());
        return UserResponse.from(userRepository.save(user));
    }

    @Transactional
    public void delete(Long id) {
        var user = userRepository.findById(id)
            .orElseThrow(() -> new EntityNotFoundException("User", id));
        userRepository.delete(user);
    }
}

@Transactional(readOnly = true) on the class sets a safe default; @Transactional on write methods overrides it. Avoid @Transactional on controllers — it spans too wide a scope and complicates error handling.

Constructor injection

// Prefer (with Lombok @RequiredArgsConstructor or explicit constructor)
@Service
public class OrderService {

    private final OrderRepository orderRepository;
    private final InvoiceService invoiceService;

    public OrderService(OrderRepository orderRepository, InvoiceService invoiceService) {
        this.orderRepository = orderRepository;
        this.invoiceService = invoiceService;
    }
}

// Avoid — field injection hides dependencies and makes testing harder
@Service
public class OrderService {
    @Autowired private OrderRepository orderRepository;  // DON'T
}

Configuration with @ConfigurationProperties

// application.yml
// app:
//   mail:
//     host: smtp.example.com
//     port: 587
//     from: noreply@example.com

@ConfigurationProperties(prefix = "app.mail")
public record MailProperties(String host, int port, String from) {}

// Register it
@SpringBootApplication
@EnableConfigurationProperties(MailProperties.class)
public class Application { ... }

// Inject like any bean
@Service
@RequiredArgsConstructor
public class MailService {
    private final MailProperties mailProperties;
}

Use @ConfigurationProperties records for any group of related configuration values. Avoid @Value on fields for groups — it scatters configuration and makes testing harder.

Bean Validation on DTOs

public record CreateProductRequest(
    @NotBlank(message = "name is required") String name,
    @NotNull @Positive BigDecimal price,
    @NotNull @Min(0) Integer stock,
    @NotBlank @Pattern(regexp = "[A-Z]{3}") String currencyCode
) {}

Activate in controllers with @Valid on @RequestBody / @PathVariable / @ModelAttribute. Spring Boot auto-configures MethodArgumentNotValidException handling — provide a @RestControllerAdvice to shape the error response.

Error handling with @RestControllerAdvice

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(EntityNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ProblemDetail handleNotFound(EntityNotFoundException ex) {
        var problem = ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, ex.getMessage());
        problem.setTitle("Resource Not Found");
        return problem;
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
    public ProblemDetail handleValidation(MethodArgumentNotValidException ex) {
        var errors = ex.getBindingResult().getFieldErrors().stream()
            .map(e -> e.getField() + ": " + e.getDefaultMessage())
            .toList();
        var problem = ProblemDetail.forStatusAndDetail(
            HttpStatus.UNPROCESSABLE_ENTITY, "Validation failed");
        problem.setProperty("errors", errors);
        return problem;
    }
}

Use ProblemDetail (Spring Boot 3.x, RFC 9457) for structured error responses. For Spring Boot 2.x, return a custom ErrorResponse record.

Domain exceptions

public class EntityNotFoundException extends RuntimeException {
    public EntityNotFoundException(String entityName, Object id) {
        super(entityName + " not found: " + id);
    }
}

public class DuplicateEmailException extends RuntimeException {
    public DuplicateEmailException(String email) {
        super("Email already registered: " + email);
    }
}

Keep domain exceptions in a dedicated package (exception or error). Do not catch and re-throw RuntimeException unless translating (e.g., wrapping a JPA exception into a domain exception).

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Angular 18-21 project structure, standalone components vs NgModule, control flow (@if/@for/@switch + *ngIf/*ngFor legacy), decorators, dependency injection (inject() function), lifecycle hooks, pipes, Angular Universal SSR pointer. Use this skill to: - Detect project style (standalone vs NgModule) and apply matching patterns. - Pick correct decorators and DI approach. - Use modern control flow (@if/@for/@switch) in Angular 17+ projects. - Apply `inject()` function over constructor injection where appropriate. - Wire bootstrap correctly (bootstrapApplication for standalone, AppModule for legacy). Do NOT use this skill for: - State management (see angular-state-and-rx). - Routing (see angular-routing). - Forms (see angular-forms). - Testing (see angular-testing).

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

AratKruglik/claude-sdlc472026년 10월 17일 업데이트

Angular forms: Reactive Forms (preferred — typed FormGroup/FormControl since Angular 14, FormBuilder, custom + async validators, FormArray, multi-step) and Template-driven (`[(ngModel)]` + FormsModule). Validation strategies, server error mapping, accessibility. Use this skill to: - Build Reactive Forms with typed FormGroup/FormControl. - Use FormBuilder для concise syntax. - Implement custom synchronous and async validators. - Wire FormArray for dynamic field lists. - Map server errors back to form fields. - Pick Reactive vs Template-driven (prefer Reactive). Do NOT use this skill for: - General conventions (see angular-conventions). - State management beyond forms (see angular-state-and-rx). - Routing (see angular-routing). - Testing forms (see angular-testing).

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

AratKruglik/claude-sdlc472026년 10월 17일 업데이트

Angular Router (built-in `@angular/router`) — route configuration for standalone and NgModule projects, functional guards (Angular 14.1+), lazy loading, route resolvers, typed params via signals/observables, programmatic navigation, route data and meta. Use this skill to: - Configure routes (standalone-style or NgModule-style). - Use functional guards (canActivate as function, preferred over class-based in 17+). - Lazy-load components or feature modules. - Implement auth guards via route meta + functional guards. - Read params/queries via `inject(ActivatedRoute)` + signals or RxJS. Do NOT use this skill for: - General conventions (see angular-conventions). - State management (see angular-state-and-rx). - Forms (see angular-forms). - Testing routes (see angular-testing).

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

AratKruglik/claude-sdlc472026년 10월 17일 업데이트

State management for Angular 18-21: signals (signal/computed/effect), services-as-state, NgRx Store + Effects + Selectors, NgRx Component Store, NgRx Signals (newer signal-based store). RxJS essentials — operators, async pipe, takeUntilDestroyed, signal/observable interop. Use this skill to: - Pick the right state tool (signals / services / NgRx variant / vue-query equivalent). - Use signals correctly (signal/computed/effect — when each). - Build a Pinia-style service-as-state singleton. - Set up NgRx Store + Effects + Selectors. - Use RxJS without leaking subscriptions (async pipe, takeUntilDestroyed, Subject patterns). - Bridge signals ↔ observables via toSignal / toObservable. Do NOT use this skill for: - General Angular conventions (see angular-conventions). - Routing state (see angular-routing). - Form state (see angular-forms). - Testing state (see angular-testing).

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

AratKruglik/claude-sdlc472026년 10월 17일 업데이트

Testing Angular 18-21: TestBed, component harnesses (@angular/cdk/testing), Karma+Jasmine (default historical) vs Jest (jest-preset-angular, modern), Angular Testing Library (RTL-style). HttpClient mocking via HttpTestingController. NgRx Effects testing. Cypress / Playwright e2e. Use this skill to: - Detect runner (Karma+Jasmine vs Jest) and configure correctly. - Write component tests with TestBed. - Use component harnesses for Material / custom UI components. - Mock HttpClient via provideHttpClientTesting + HttpTestingController. - Test signal-based inputs with componentRef.setInput(). - Test NgRx Effects with provideMockActions. Do NOT use this skill for: - General Angular conventions (see angular-conventions). - Routing patterns broadly (see angular-routing — covers testing routes briefly). - Form patterns broadly (see angular-forms).

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

AratKruglik/claude-sdlc472026년 10월 17일 업데이트

Shared conventions for every SDLC development-phase architect agent: hard rules, code quality bar, workflow steps (superpowers invocation, spec reading, codebase exploration, verification), and the report/compact-summary contract. Architects load this skill first, then apply their stack-specific instructions on top.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

AratKruglik/claude-sdlc472026년 10월 17일 업데이트

AratKruglik의 스킬 모두 보기

이 스킬의 문제 신고