Standardizes coding conventions for Spring Framework 7 and Boot 4, emphasizing feature-based package layouts and modern async patterns.
Install
mkdir -p .claude/skills/spring-boot-4-conventions && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/13398" && unzip -o skill.zip -d .claude/skills/spring-boot-4-conventions && rm skill.zipInstalls to .claude/skills/spring-boot-4-conventions
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Spring Framework 7 / Spring Boot 4 idioms and defaults. Use when writing or reviewing controllers, services, configuration, HTTP clients, async/virtual-thread code, or anything touching Spring's programming model.Key capabilities
- →Organize packages by feature, not by layer
- →Use constructor injection for dependencies
- →Prefer `@HttpExchange` declarative clients for outbound HTTP
- →Configure virtual threads
- →Use Java 25 features like records and sealed types
How it works
The skill applies Spring Framework 7 and Spring Boot 4 idioms and defaults, guiding package layout, dependency injection, HTTP client choices, and Java 25 feature usage.
Inputs & outputs
When to use spring-boot-4-conventions
- →Write Spring 7 controllers/services
- →Apply feature-based package layout
- →Review Spring Boot 4 PRs
- →Configure virtual threads
About this skill
Spring Framework 7 / Spring Boot 4 conventions
Java 25 + Spring Framework 7 + Spring Boot 4. Prefer the most modern idiom unless an ADR explains otherwise.
Defaults to apply
Package layout — by feature, not by layer
Top-level packages are bounded contexts / features, not technical layers.
Inside a feature, split by visibility (api published, private impl hidden).
For the private impl sub-packages, apply the following rule:
- One class of a given type (single entity, single service, etc.) — keep it directly in the feature package (no sub-package).
- Multiple classes of the same type — use a typed sub-package within the feature:
model/— JPA entitiesrepository/— Spring Data repositoriesservice/— service classes / implementationsservice/— service implementations
- Inside
api/— keep the controller (or service interface) at theapi/level; put all request/response DTO records in anapi/dto/sub-package.
All classes in the feature's private sub-packages (model, repository, service, internal) must be public (cross-package visibility is required when code spans multiple sub-packages). Cross-feature access is still forbidden and enforced by ArchUnit — public here means "visible within this feature", not "part of the published API".
The api/ sub-package is the only published surface. Other features depend only on <feature>.api.
com.example.checkout
├── giftcard/ # feature/domain — one package per bounded context
│ ├── api/ # published surface: controller, service interfaces, events
│ │ ├── GiftCardController.java
│ │ ├── GiftCardRedemptionService.java
│ │ ├── GiftCardRedeemed.java
│ │ ├── dto/ # request/response DTOs always in api/dto/
│ │ │ ├── RedeemCommand.java
│ │ │ └── GiftCardRedemptionResponse.java
│ │ └── exception/ # domain exceptions thrown by this feature's services
│ │ └── InsufficientBalanceException.java
│ ├── model/ # entities (multiple → typed sub-package)
│ │ ├── GiftCardEntity.java
│ │ └── GiftCardTransactionEntity.java
│ ├── repository/ # repositories (multiple → typed sub-package)
│ │ ├── GiftCardRepository.java
│ │ └── GiftCardTransactionRepository.java
│ └── service/ # service implementations (multiple → typed sub-package)
│ └── GiftCardRedemptionServiceImpl.java
├── order/ # another feature
│ ├── api/
│ │ └── dto/ # DTOs always in api/dto/
│ │ └── OrderSummaryResponse.java
│ └── service/ # single service impl — sub-package still used for consistency
│ └── OrderService.java
└── shared/ # cross-cutting: error envelope, security config, time
└── exception/ # GlobalExceptionHandler (@RestControllerAdvice)
Forbidden layouts (do not create these at the application root level):
com.example.checkout
├── controller/ ❌ by-layer at root
├── service/ ❌ by-layer at root
├── repository/ ❌ by-layer at root
└── model/ ❌ by-layer at root
The forbidden layouts are top-level by-layer packages. Typed sub-packages
(model/, repository/, service/) are allowed — and encouraged when there
are multiple classes — inside a feature/domain package.
Why:
- Features change together; layers don't. By-feature keeps the diff for one change inside one package.
- Typed sub-packages within a feature improve navigation when a bounded context grows beyond 4–5 private classes.
- ArchUnit enforces that no other feature reaches into
model/,repository/, orservice/sub-packages (seearchunit-rules). - It maps 1:1 to the module boundaries enforced by
archunit-rules.
Cross-feature interaction:
- Other features depend only on
<feature>.api. - Prefer events (
ApplicationEventPublisher) for fire-and-forget integration. - Direct dependencies between features are explicit and asserted by ArchUnit
(e.g.
ordermay depend ongiftcard.api, never the reverse).
Dependency injection
- Constructor injection only. No field
@Autowired. No setter injection except where Spring forces it (e.g., a pre-existing framework callback). - One
finalfield per dependency. Write the constructor explicitly — Lombok is forbidden (see No Lombok below).
@Service
public class CheckoutService {
private final OrderRepository orders;
private final GiftCardClient giftCards;
public CheckoutService(OrderRepository orders, GiftCardClient giftCards) {
this.orders = orders;
this.giftCards = giftCards;
}
}
HTTP clients
- Outbound HTTP: prefer
@HttpExchangedeclarative clients built onRestClient. UseWebClientonly when the call is genuinely reactive end-to-end. - Do not use
RestTemplatein new code. - Each external API gets its own client interface in the
infrapackage.
@HttpExchange(url = "/cards", accept = "application/json")
public interface GiftCardClient {
@GetExchange("/{code}")
GiftCard fetch(@PathVariable String code);
}
Web layer
- Controllers are
@RestController. Return DTO records, not entities. - Use Java
recordfor request/response DTOs. - Validate input at the controller boundary:
- Annotate the controller class with
@Validated. - Annotate every
@RequestBodyparameter with@Valid. - Annotate every
@PathVariableand@RequestParamwith the appropriate Bean Validation constraint (@Positive,@NotBlank,@Max, etc.). - Always add tests that exercise each constraint; a constraint with no test is untested behaviour.
- Annotate the controller class with
- Never use
Pageableas a controller parameter.Pageableis a Spring Data internal type; exposing it on the API surface leaks persistence concerns, makes parameters implicit to callers, and prevents OpenAPI tooling from generating correct query-param docs. Instead:- Declare explicit
@RequestParamfields (page,size, sort params). - Apply Bean Validation constraints directly (
@PositiveOrZero,@Positive,@Max(100)). - Construct
PageRequest.of(page, size)inside the method and pass it to the service.
@GetMapping FooPageResponse list( @RequestParam(defaultValue = "0") @PositiveOrZero int page, @RequestParam(defaultValue = "25") @Positive @Max(100) int size) { return fooService.getPage(PageRequest.of(page, size)); } - Declare explicit
- Avoid
ResponseEntity<T>as a return type unless varying the HTTP status code at runtime is unavoidable. For the common cases:- Add a response header (e.g.
ETag,Location) → injectHttpServletResponseand callresponse.setHeader(...), then return the DTO directly. - 201 Created with Location → use
@ResponseStatus(HttpStatus.CREATED)on the method + set theLocationheader viaHttpServletResponse. ResponseEntityis acceptable only in handlers that need to conditionally vary the status (e.g.304 Not Modifiedbased onIf-None-Match).
- Add a response header (e.g.
- Map exceptions via
@RestControllerAdviceto a single, documented error envelope.
// in <feature>/api/dto/
public record ApplyGiftCardRequest(@NotBlank String code, @Min(0) int orderTotalCents) {}
public record ApplyGiftCardResponse(int redeemedCents, int newOrderTotalCents) {}
Persistence
- Spring Data JPA is the default. Use
@Queryfor non-trivial reads; never rely on derived queries longer than three predicates. - Always paginate list endpoints (
Pageable). Reject unbounded queries. - Use
@ServiceConnectionwith Testcontainers in tests instead ofapplication-test.ymloverrides. - Use
Instantfor audit timestamps (created_at,updated_at), notLocalDateTime.Instantis an absolute UTC point in time;LocalDateTimeis ambiguous across DST transitions and timezone changes. Hibernate 6 mapsInstanttoDATETIME/TIMESTAMPcolumns using UTC normalization — no schema change required.
Virtual threads
- Spring Boot 4 enables virtual threads for the web server by default. Keep it on. Do not introduce custom
Executors that pin to platform threads without measuring.
Configuration
- One
@ConfigurationPropertiesrecord per bounded module. Validate with@Validated. - No
@Valuefor grouped settings. Use@Valueonly for ad-hoc single primitives.
@ConfigurationProperties("app.giftcard")
@Validated
public record GiftCardProperties(@NotBlank String baseUrl, @Min(1) int timeoutMs) {}
Observability
- Structured logging via
Logger+ key/value pairs (Boot 4 nativeStructuredLoggingFormatter). - Metrics via
MeterRegistry; one counter per business outcome (gift_card.redeemed,gift_card.rejected).
AOT-friendly
- No reflection on application code.
- Beans are
@Component,@Service, etc. at compile time. No runtime registration. - Anything dynamic is registered via a
RuntimeHintsRegistrarand noted in the design doc.
Anti-patterns to flag in review
- Field injection (
@Autowiredon a field). - Lombok in any form (
@Data,@Getter,@Setter,@Builder,@RequiredArgsConstructor,@Slf4j,@SneakyThrows, etc.) — see No Lombok below. - Top-level packages named
controller,service,repository,model,dto,util(by-layer layout at the application root) — use feature/domain packages instead (see Package layout). Note:model/,repository/, andservice/sub-packages within a feature package are allowed when there are multiple classes of that type. RestTemplatein new code.- Returning entities from controllers.
- Untyped
Map<String, Object>request/response bodies. - Catching
Exceptionthen rethrowingRuntimeExceptionwith no message. @SpringBootTestfor code that a slice test (@WebMvcTest,@DataJpaTest) can cover.Pageableas a controller method parameter — use
Content truncated.
When not to use it
- →When using Lombok in new code
- →When using `RestTemplate` in new code
- →When using field `@Autowired` or setter injection
Limitations
- →Lombok is forbidden in new code.
- →`RestTemplate` is not to be used in new code.
- →Field `@Autowired` and setter injection are not preferred.
How it compares
This skill enforces modern Spring conventions like feature-first package organization and declarative HTTP clients, moving away from older patterns like layer-based packages and `RestTemplate`.
Compared to similar skills
spring-boot-4-conventions side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| spring-boot-4-conventions (this skill) | 0 | 3mo | No flags | Advanced |
| java-pro | 34 | 4mo | No flags | Advanced |
| backend-microservice-development | 2 | 3mo | No flags | Intermediate |
| microservice-infrastructure | 4 | 3mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by loiane
View all by loiane →You might also like
java-pro
sickn33
Master Java 21+ with modern features like virtual threads, pattern matching, and Spring Boot 3.x. Expert in the latest Java ecosystem including GraalVM, Project Loom, and cloud-native patterns. Use PROACTIVELY for Java development, microservices architecture, or performance optimization.
backend-microservice-development
TencentBlueKing
后端微服务开发规范,涵盖目录结构、分层架构(API/Service/DAO)、依赖注入、配置管理、Spring Boot 最佳实践。当用户进行后端开发、创建新微服务、编写 Kotlin/Java 代码或设计服务架构时使用。
microservice-infrastructure
TencentBlueKing
微服务基础设施指南,涵盖条件配置、事件驱动架构、服务间通信、国际化与日志等微服务架构的核心基础设施。当用户实现服务间调用、配置多环境、实现异步通信、处理国际化或规范日志输出时使用。
common-technical-practices
TencentBlueKing
通用技术实践指南,涵盖 AOP 切面、分布式锁、重试机制、参数校验、性能监控、定时任务、审计日志等后端开发中的常见技术实践。当用户需要实现横切关注点、处理并发控制、配置重试策略、添加性能监控或实现审计功能时使用。
clean-architecture
darshanchaudharii
Provides implementation patterns for Clean Architecture, Hexagonal Architecture (Ports & Adapters), and Domain-Driven Design in Java 21+ Spring Boot 3.5+ applications. Use when structuring layered architectures, separating domain logic from frameworks, implementing ports and adapters, creating entit
Context7 MCP Rule
dlwlrmjlo
Always use the Context7 MCP server when foundational planning or architecture decisions are being made.