Skip to content
Skill · Windsurf.windsurfrulesCoding

Windsurf Rules for Spring Boot Services

Spring Boot rules enforcing constructor injection, record-based DTOs mapped at the controller edge, and Testcontainers instead of mocked repositories.

Use when: Use in a Spring Boot 3.x / Java 17+ service when you want Windsurf to stop suggesting field injection, entity leakage into controllers, and H2-based integration tests.

@libraryNewcomer
0 upvotes0 forks0 comments

The skill file

Windsurf · .windsurfrules
.windsurfrules3141 chars
# Spring Boot Service Rules ## Dependency injection - Constructor injection only. `@Autowired` on fields or setters is forbidden; with a single constructor the annotation is unnecessary — omit it. - All injected fields are `private final`. If a class needs more than 5 dependencies, split it instead of adding a sixth. - No `@Component` on classes that hold no state and have one public static-like method — make it a plain class or static utility. - Never use `ApplicationContext.getBean()` or `@Lazy` to dodge a circular dependency; restructure the cycle. ## DTOs and mapping - JPA entities never cross the controller boundary in either direction. Controllers accept and return `record` DTOs only. - Request and response DTOs are separate records (`CreateOrderRequest`, `OrderResponse`) — never reuse one record for both. - Mapping lives in a dedicated mapper (MapStruct or a small hand-written class in the same package as the DTO). No mapping logic inside controllers or services. - Validate request DTOs with `jakarta.validation` annotations plus `@Valid` on the controller parameter; do not re-validate the same constraints in the service. - DTO fields that can legitimately be absent use `Optional` accessors or explicit nullability comments — never silent `null` propagation through three layers. ## Persistence - Repository interfaces declare intent-revealing methods (`findActiveByCustomerId`); avoid `@Query` with string concatenation, and never build JPQL from user input. - `@Transactional` goes on service methods, never on controllers or repositories. Mark read paths `@Transactional(readOnly = true)`. - Schema changes happen only through Flyway migrations under `db/migration`; `ddl-auto` stays at `validate` in every profile, including tests. - Fetch strategy is `LAZY` for all associations; load what you need with `@EntityGraph` or fetch joins. ## Error handling - One `@RestControllerAdvice` owns all exception-to-response mapping. Controllers and services never build `ResponseEntity` error bodies inline. - Throw specific exceptions (`OrderNotFoundException extends NoSuchElementException`), never bare `RuntimeException` with a message. - Error responses follow RFC 7807 problem-detail shape via `ProblemDetail`. ## Testing with Testcontainers - Integration tests run against a real PostgreSQL Testcontainer via `@ServiceConnection`. H2 is forbidden everywhere. - Define one shared abstract base class (or `@TestConfiguration`) that starts a single reusable container for the whole suite — never one container per test class. - Repository and service tests use the container; only pure mapping/calculation logic gets Mockito-free plain unit tests. - Web-layer tests use `@WebMvcTest` with mocked services; full-stack happy paths use `@SpringBootTest` + the container. - Every Flyway migration must be exercised by the test suite booting against the container — a migration that breaks startup must fail CI. ## Workflow - Run `./mvnw verify` (or `./gradlew check`) before suggesting a commit; format with Spotless if configured. - Never edit files under `target/`, `build/`, or generated MapStruct sources.

Install

drop into your repo

Save this to your project or home directory so Windsurf can load it.

path./.windsurfrules
Discussion

What people are saying

Forks

0 forks of this skill
Loading forks…