К основному содержанию
скилл CX Codex

spring-boot-patterns — скилл для Claude Code: слои Spring Boot

Скилл задаёт структуру Spring Boot-проекта и шаблоны для контроллеров, сервисов и репозиториев. Пригодится, когда агент пишет новый код или разбирает чужой.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add decebals/claude-code-java --skill spring-boot-patterns -a claude-code
Открыть на GitHub

Что делает spring-boot-patterns

Скилл описывает раскладку пакетов: controller, service с подпакетом impl, repository, model, dto с request и response, exception, config, util. Дальше идут готовые шаблоны — REST-контроллер с версионированным путём /api/v1/users, интерфейс сервиса плюс реализация, JPA-репозиторий с производными запросами и @Query, DTO-записи с валидацией через @NotBlank, @Email, @Min и маппер MapStruct.

Отдельный блок — антипаттерны. Бизнес-логика в контроллере, прямой доступ к репозиторию из контроллера, возврат сущности наружу вместо DTO: всё это помечено как ошибка. Ещё скилл напоминает про @Transactional(readOnly = true) на уровне класса и @Transactional на методах записи.

Про JPA и Hibernate здесь ничего нет — автор прямо отправляет за этим в скилл jpa-patterns. Если проект на голом Spring без Boot или на другом фреймворке, шаблоны не подойдут. Для работы нужен сам агент с поддержкой скиллов; в примерах используется Lombok (@RequiredArgsConstructor) и MapStruct, так что эти зависимости должны быть в проекте.

Какие задачи решает

  • разложить пакеты нового Spring Boot-проекта по слоям
  • написать REST-контроллер с версионированным путём и кодами ответов
  • вынести бизнес-логику из контроллера в сервис
  • заменить возврат сущности на DTO с маппером
  • проверить чужой код на типичные нарушения слоёв

Примеры запросов агенту

  • создай контроллер для заказов по шаблону spring-boot-patterns
  • добавь сервис и репозиторий для сущности User, как в скилле spring-boot-patterns
  • проверь этот класс на антипаттерны из spring-boot-patterns

Как установить spring-boot-patterns

1Через Skills CLI (Vercel)

npx skills add decebals/claude-code-java --skill spring-boot-patterns -a claude-code

2Вручную: положить файлы

~/.claude/skills/spring-boot-patterns/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/spring-boot-patterns/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Из того же набора10

clean-code Скилл учит агента принципам DRY, KISS и YAGNI, правилам именования и структурированию функций. Помогает при рефакторинге запутанного кода с длинными методами. CX Codex +1 379установок concurrency-review Находит гонки данных, блокировки и скрытые дефекты многопоточности в коде на Java. Проверяет как классические примитивы синхронизации, так и Virtual Threads. CX Codex +1 345установок solid-principles Скилл проверяет Java-код на соответствие принципам SOLID и помогает разгрузить разросшиеся классы. Пригодится при проектировании архитектуры и ревью дизайна. CX Codex +1 345установок architecture-review Скилл для макроразбора Java-проекта: структура пакетов, границы модулей, направление зависимостей и слои. Пригодится, когда кодовая база разрослась и непонятно, где что лежит. CX Codex +1 338установок design-patterns Справочник паттернов проектирования на Java с готовыми шаблонами кода. Помогает агенту внедрять Builder, Factory или Strategy при рефакторинге и проектировании архитектуры. CX Codex +1 336установок java-migration Ведёт проект по цепочке LTS-версий Java: от оценки текущего состояния до правки сборки и тестов. Пригодится, когда надо перевести легаси на Java 21 или 25. CX Codex +1 321установок performance-smell-detection Находит узкие места в Java-коде: создание лишних объектов и компиляцию regex в циклах. Помогает при ревью критичных к нагрузке участков. CX Codex +1 310установок security-audit Чек-лист по OWASP Top 10 для Java-кода: проверяет валидацию ввода, SQL-инъекции, XSS и настройки безопасности. Пригодится перед релизом и при ревью авторизации. CX Codex +1 309установок

Похожие

spring-boot-crud-patterns giuseppe-trisciuoglio Скилл собирает полный CRUD-слой для сервисов на Spring Boot 3: агрегаты, JPA-репозитории, DTO и REST-контроллеры по feature-архитектуре. CX Codex +1 3 900установок spring-boot-rest-api-standards giuseppe-trisciuoglio Задаёт правила проектирования REST API в Spring Boot: от структуры URL и статус-кодов до валидации DTO и перехвата ошибок. CX Codex +1 4 200установок spring-boot-development manutej Скилл для ИИ-агента, который подсказывает, как писать Spring Boot: авто-конфигурация, внедрение зависимостей, REST-контроллеры, Spring Data и Spring Security. CX Codex +1 534установок spring-boot-engineer https://github.com/Jeffallan Разворачивает архитектуру Spring Boot 3.x, контроллеры, репозитории Spring Data JPA и правила Spring Security 6. Пригодится при сборке REST API, реактивных сервисов и микросервисов на Java. CX Codex +1 8 400установок java-spring-boot pluginagentmarketplace Скилл помогает создавать бэкенд на Spring Boot 3: настраивать REST API, аутентификацию через Spring Security, доступ к базам через Spring Data и мониторинг Actuator. CX Codex +1 11 800установок spring-boot Mindrally Скилл задаёт агенту стиль кода на Spring Boot: REST-контроллеры, тесты, безопасность, сборка и деплой. Пригодится, когда проект на Java и хочется единообразия. CX Codex +1 832установок

Рядом в теме «Бэкенд и API»

Всё по теме «Бэкенд и API» →

Вопросы о spring-boot-patterns

Как установить spring-boot-patterns в Claude?

Выполните npx skills add decebals/claude-code-java --skill spring-boot-patterns -a claude-code. Файл скилла окажется по пути ~/.claude/skills/spring-boot-patterns/SKILL.md.

Что делает spring-boot-patterns?

Даёт агенту шаблоны и правила для Spring Boot: структуру пакетов, контроллеры, сервисы, репозитории, DTO и обработку исключений. Плюс список антипаттернов, которых стоит избегать.

Чем spring-boot-patterns отличается от jpa-patterns?

Первый про слои и wiring приложения, второй — про поведение JPA и Hibernate. Для вопросов о сущностях и запросах автор советует брать jpa-patterns.

Исходный текст

SKILL.md · MIT
---
name: spring-boot-patterns
description: Spring Boot best practices and patterns. Use when creating controllers, services or repositories, or when the user asks about Spring Boot layering, wiring, configuration or exception handling. For JPA and Hibernate behaviour, use jpa-patterns instead.
license: MIT
---

# Spring Boot Patterns Skill

Best practices and patterns for Spring Boot applications.

## When to Use
- User says "create controller" / "add service" / "Spring Boot help"
- Reviewing Spring Boot code
- Setting up new Spring Boot project structure

## Project Structure

```
src/main/java/com/example/myapp/
├── MyAppApplication.java          # @SpringBootApplication
├── config/                        # Configuration classes
│   ├── SecurityConfig.java
│   └── WebConfig.java
├── controller/                    # REST controllers
│   └── UserController.java
├── service/                       # Business logic
│   ├── UserService.java
│   └── impl/
│       └── UserServiceImpl.java
├── repository/                    # Data access
│   └── UserRepository.java
├── model/                         # Entities
│   └── User.java
├── dto/                           # Data transfer objects
│   ├── request/
│   │   └── CreateUserRequest.java
│   └── response/
│       └── UserResponse.java
├── exception/                     # Custom exceptions
│   ├── ResourceNotFoundException.java
│   └── GlobalExceptionHandler.java
└── util/                          # Utilities
    └── DateUtils.java
```

---

## Controller Patterns

### REST Controller Template
```java
@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor  // Lombok for constructor injection
public class UserController {

    private final UserService userService;

    @GetMapping
    public ResponseEntity<List<UserResponse>> getAll() {
        return ResponseEntity.ok(userService.findAll());
    }

    @GetMapping("/{id}")
    public ResponseEntity<UserResponse> getById(@PathVariable Long id) {
        return ResponseEntity.ok(userService.findById(id));
    }

    @PostMapping
    public ResponseEntity<UserResponse> create(
            @Valid @RequestBody CreateUserRequest request) {
        UserResponse created = userService.create(request);
        URI location = ServletUriComponentsBuilder.fromCurrentRequest()
            .path("/{id}")
            .buildAndExpand(created.getId())
            .toUri();
        return ResponseEntity.created(location).body(created);
    }

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

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable Long id) {
        userService.delete(id);
        return ResponseEntity.noContent().build();
    }
}
```

### Controller Best Practices

| Practice | Example |
|----------|---------|
| Versioned API | `/api/v1/users` |
| Plural nouns | `/users` not `/user` |
| HTTP methods | GET=read, POST=create, PUT=update, DELETE=delete |
| Status codes | 200=OK, 201=Created, 204=NoContent, 404=NotFound |
| Validation | `@Valid` on request body |

### ❌ Anti-patterns
```java
// ❌ Business logic in controller
@PostMapping
public User create(@RequestBody User user) {
    user.setCreatedAt(LocalDateTime.now());  // Logic belongs in service
    return userRepository.save(user);         // Direct repo access
}

// ❌ Returning entity directly (exposes internals)
@GetMapping("/{id}")
public User getById(@PathVariable Long id) {
    return userRepository.findById(id).get();
}
```

---

## Service Patterns

### Service Interface + Implementation
```java
// Interface
public interface UserService {
    List<UserResponse> findAll();
    UserResponse findById(Long id);
    UserResponse create(CreateUserRequest request);
    UserResponse update(Long id, UpdateUserRequest request);
    void delete(Long id);
}

// Implementation
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)  // Default read-only
public class UserServiceImpl implements UserService {

    private final UserRepository userRepository;
    private final UserMapper userMapper;

    @Override
    public List<UserResponse> findAll() {
        return userRepository.findAll().stream()
            .map(userMapper::toResponse)
            .toList();
    }

    @Override
    public UserResponse findById(Long id) {
        return userRepository.findById(id)
            .map(userMapper::toResponse)
            .orElseThrow(() -> new ResourceNotFoundException("User", id));
    }

    @Override
    @Transactional  // Write transaction
    public UserResponse create(CreateUserRequest request) {
        User user = userMapper.toEntity(request);
        User saved = userRepository.save(user);
        return userMapper.toResponse(saved);
    }

    @Override
    @Transactional
    public void delete(Long id) {
        if (!userRepository.existsById(id)) {
            throw new ResourceNotFoundException("User", id);
        }
        userRepository.deleteById(id);
    }
}
```

### Service Best Practices

- Interface + Impl for testability
- `@Transactional(readOnly = true)` at class level
- `@Transactional` for write methods
- Throw domain exceptions, not generic ones
- Use mappers (MapStruct) for entity ↔ DTO conversion

---

## Repository Patterns

### JPA Repository
```java
public interface UserRepository extends JpaRepository<User, Long> {

    // Derived query
    Optional<User> findByEmail(String email);

    List<User> findByActiveTrue();

    // Custom query
    @Query("SELECT u FROM User u WHERE u.department.id = :deptId")
    List<User> findByDepartmentId(@Param("deptId") Long departmentId);

    // Native query (use sparingly)
    @Query(value = "SELECT * FROM users WHERE created_at > :date",
           nativeQuery = true)
    List<User> findRecentUsers(@Param("date") LocalDate date);

    // Exists check (more efficient than findBy)
    boolean existsByEmail(String email);

    // Count
    long countByActiveTrue();
}
```

### Repository Best Practices

- Use derived queries when possible
- `Optional` for single results
- `existsBy` instead of `findBy` for existence checks
- Avoid native queries unless necessary
- Use `@EntityGraph` for fetch optimization

---

## DTO Patterns

### Request/Response DTOs
```java
// Request DTO with validation
public record CreateUserRequest(
    @NotBlank(message = "Name is required")
    @Size(min = 2, max = 100)
    String name,

    @NotBlank
    @Email(message = "Invalid email format")
    String email,

    @NotNull
    @Min(18)
    Integer age
) {}

// Response DTO
public record UserResponse(
    Long id,
    String name,
    String email,
    LocalDateTime createdAt
) {}
```

### MapStruct Mapper
```java
@Mapper(componentModel = "spring")
public interface UserMapper {

    UserResponse toResponse(User entity);

    List<UserResponse> toResponseList(List<User> entities);

    @Mapping(target = "id", ignore = true)
    @Mapping(target = "createdAt", ignore = true)
    User toEntity(CreateUserRequest request);
}
```

---

## Exception Handling

### Custom Exceptions
```java
public class ResourceNotFoundException extends RuntimeException {

    public ResourceNotFoundException(String resource, Long id) {
        super(String.format("%s not found with id: %d", resource, id));
    }
}

public class BusinessException extends RuntimeException {

    private final String code;

    public BusinessException(String code, String message) {
        super(message);
        this.code = code;
    }
}
```

### Global Exception Handler
```java
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
        log.warn("Resource not found: {}", ex.getMessage());
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
            .body(new ErrorResponse("NOT_FOUND", ex.getMessage()));
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handleValidation(
            MethodArgumentNotValidException ex) {
        List<String> errors = ex.getBindingResult().getFieldErrors().stream()
            .map(e -> e.getField() + ": " + e.getDefaultMessage())
            .toList();
        return ResponseEntity.badRequest()
            .body(new ErrorResponse("VALIDATION_ERROR", errors.toString()));
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGeneric(Exception ex) {
        log.error("Unexpected error", ex);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
            .body(new ErrorResponse("INTERNAL_ERROR", "An unexpected error occurred"));
    }
}

public record ErrorResponse(String code, String message) {}
```

---

## Configuration Patterns

### Application Properties
```yaml
# application.yml
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/mydb
    username: ${DB_USER}
    password: ${DB_PASSWORD}
  jpa:
    hibernate:
      ddl-auto: validate  # Never 'create' in production!
    show-sql: false

app:
  jwt:
    secret: ${JWT_SECRET}
    expiration: 86400000
```

### Configuration Properties Class
```java
@Configuration
@ConfigurationProperties(prefix = "app.jwt")
@Validated
public class JwtProperties {

    @NotBlank
    private String secret;

    @Min(60000)
    private long expiration;

    // getters and setters
}
```

### Profile-Specific Configuration
```
src/main/resources/
├── application.yml           # Common config
├── application-dev.yml       # Development
├── application-test.yml      # Testing
└── application-prod.yml      # Production
```

---

## Common Annotations Quick Reference

| Annotation | Purpose |
|------------|---------|
| `@RestController` | REST controller (combines @Controller + @ResponseBody) |
| `@Service` | Business logic component |
| `@Repository` | Data access component |
| `@Configuration` | Configuration class |
| `@RequiredArgsConstructor` | Lombok: constructor injection |
| `@Transactional` | Transaction management |
| `@Valid` | Trigger validation |
| `@ConfigurationProperties` | Bind properties to class |
| `@Profile("dev")` | Profile-specific bean |
| `@Scheduled` | Scheduled tasks |

---

## Testing Patterns

### Controller Test (MockMvc)
```java
@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Test
    void shouldReturnUser() throws Exception {
        when(userService.findById(1L))
            .thenReturn(new UserResponse(1L, "John", "[email protected]", null));

        mockMvc.perform(get("/api/v1/users/1"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.name").value("John"));
    }
}
```

### Service Test
```java
@ExtendWith(MockitoExtension.class)
class UserServiceImplTest {

    @Mock
    private UserRepository userRepository;

    @Mock
    private UserMapper userMapper;

    @InjectMocks
    private UserServiceImpl userService;

    @Test
    void shouldThrowWhenUserNotFound() {
        when(userRepository.findById(1L)).thenReturn(Optional.empty());

        assertThatThrownBy(() -> userService.findById(1L))
            .isInstanceOf(ResourceNotFoundException.class);
    }
}
```

### Integration Test
```java
@SpringBootTest
@AutoConfigureMockMvc
@Testcontainers
class UserIntegrationTest {

    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");

    @Autowired
    private MockMvc mockMvc;

    @Test
    void shouldCreateUser() throws Exception {
        mockMvc.perform(post("/api/v1/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content("""
                    {"name": "John", "email": "[email protected]", "age": 25}
                    """))
            .andExpect(status().isCreated());
    }
}
```

---

## Quick Reference Card

| Layer | Responsibility | Annotations |
|-------|---------------|-------------|
| Controller | HTTP handling, validation | `@RestController`, `@Valid` |
| Service | Business logic, transactions | `@Service`, `@Transactional` |
| Repository | Data access | `@Repository`, extends `JpaRepository` |
| DTO | Data transfer | Records with validation annotations |
| Config | Configuration | `@Configuration`, `@ConfigurationProperties` |
| Exception | Error handling | `@RestControllerAdvice` |

© decebals · MIT · decebals/claude-code-java

Смотрите также

Спонсоры проекта

Компании, которые помогают держать аналитику открытой