Spring Boot Engineer
Core Workflow
- Analyze requirements — Identify service boundaries, APIs, data models, security needs
- Design architecture — Plan microservices, data access, cloud integration, security; confirm design before coding
- Implement — Create services with constructor injection and layered architecture (see Quick Start below)
- Secure — Add Spring Security, OAuth2, method security, CORS configuration; verify security rules compile and pass tests. If compilation or tests fail: review error output, fix the failing rule or configuration, and re-run before proceeding
- Test — Write unit, integration, and slice tests; run
./mvnw test (or ./gradlew test) and confirm all pass before proceeding. If tests fail: review the stack trace, isolate the failing assertion or component, fix the issue, and re-run the full suite
- Deploy — Configure health checks and observability via Actuator; validate
/actuator/health returns UP. If health is DOWN: check the components detail in the response, resolve the failing component (e.g., datasource, broker), and re-validate
Reference Guide
Load detailed guidance based on context:
| Topic |
Reference |
Load When |
| Web Layer |
references/web.md |
Controllers, REST APIs, validation, exception handling |
| Data Access |
references/data.md |
Spring Data JPA, repositories, transactions, projections |
| Security |
references/security.md |
Spring Security 6, OAuth2, JWT, method security |
| Cloud Native |
references/cloud.md |
Spring Cloud, Config, Discovery, Gateway, resilience |
| Testing |
references/testing.md |
@SpringBootTest, MockMvc, Testcontainers, test slices |
Quick Start — Minimal Working Structure
A standard Spring Boot feature consists of these layers. Use these as copy-paste starting points.
Entity
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank
private String name;
@DecimalMin("0.0")
private BigDecimal price;
// getters / setters or use @Data (Lombok)
}
Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findByNameContainingIgnoreCase(String name);
}
Service (constructor injection)
@Service
public class ProductService {
private final ProductRepository repo;
public ProductService(ProductRepository repo) { // constructor injection — no @Autowired
this.repo = repo;
}
@Transactional(readOnly = true)
public List<Product> search(String name) {
return repo.findByNameContainingIgnoreCase(name);
}
@Transactional
public Product create(ProductRequest request) {
var product = new Product();
product.setName(request.name());
product.setPrice(request.price());
return repo.save(product);
}
}
REST Controller
@RestController
@RequestMapping("/api/v1/products")
@Validated
public class ProductController {
private final ProductService service;
public ProductController(ProductService service) {
this.service = service;
}
@GetMapping
public List<Product> search(@RequestParam(defaultValue = "") String name) {
return service.search(name);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Product create(@Valid @RequestBody ProductRequest request) {
return service.create(request);
}
}
DTO (record)
public record ProductRequest(
@NotBlank String name,
@DecimalMin("0.0") BigDecimal price
) {}
Global Exception Handler
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> handleValidation(MethodArgumentNotValidException ex) {
return ex.getBindingResult().getFieldErrors().stream()
.collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
}
@ExceptionHandler(EntityNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public Map<String, String> handleNotFound(EntityNotFoundException ex) {
return Map.of("error", ex.getMessage());
}
}
Test Slice
@WebMvcTest(ProductController.class)
class ProductControllerTest {
@Autowired MockMvc mockMvc;
@MockBean ProductService service;
@Test
void createProduct_validRequest_returns201() throws Exception {
var product = new Product(); product.setName("Widget"); product.setPrice(BigDecimal.TEN);
when(service.create(any())).thenReturn(product);
mockMvc.perform(post("/api/v1/products")
.contentType(MediaType.APPLICATION_JSON)
.content("""{"name":"Widget","price":10.0}"""))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.name").value("Widget"));
}
}
Constraints
MUST DO
| Rule |
Correct Pattern |
| Constructor injection |
public MyService(Dep dep) { this.dep = dep; } |
| Validate API input |
@Valid @RequestBody MyRequest req on every mutating endpoint |
| Type-safe config |
@ConfigurationProperties(prefix = "app") bound to a record/class |
| Appropriate stereotype |
@Service for business logic, @Repository for data, @RestController for HTTP |
| Transaction scope |
@Transactional on multi-step writes; @Transactional(readOnly = true) on reads |
| Hide internals |
Catch domain exceptions in @RestControllerAdvice; return problem details, not stack traces |
| Externalize secrets |
Use environment variables or Spring Cloud Config — never application.properties |
MUST NOT DO
- Use field injection (
@Autowired on fields)
- Skip input validation on API endpoints
- Use
@Component when @Service/@Repository/@Controller applies
- Mix blocking and reactive code (e.g., calling
.block() inside a WebFlux chain)
- Store secrets or credentials in
application.properties/application.yml
- Hardcode URLs, credentials, or environment-specific values
- Use deprecated Spring Boot 2.x patterns (e.g.,
WebSecurityConfigurerAdapter)
1---2name: spring-boot-engineer3description: Generates Spring Boot 3.x configurations, creates REST controllers, implements Spring Security 6 authentication flows, sets up Spring Data JPA repositories, and configures reactive WebFlux endpoints. Use when building Spring Boot 3.x applications, microservices, or reactive Java applications; invoke for Spring Data JPA, Spring Security 6, WebFlux, Spring Cloud integration, Java REST API design, or Microservices Java architecture.4license: MIT5---6
7# Spring Boot Engineer
8
9## Core Workflow
10
111. **Analyze requirements** — Identify service boundaries, APIs, data models, security needs
122. **Design architecture** — Plan microservices, data access, cloud integration, security; confirm design before coding
133. **Implement** — Create services with constructor injection and layered architecture (see Quick Start below)
144. **Secure** — Add Spring Security, OAuth2, method security, CORS configuration; verify security rules compile and pass tests. If compilation or tests fail: review error output, fix the failing rule or configuration, and re-run before proceeding
155. **Test** — Write unit, integration, and slice tests; run `./mvnw test` (or `./gradlew test`) and confirm all pass before proceeding. If tests fail: review the stack trace, isolate the failing assertion or component, fix the issue, and re-run the full suite
166. **Deploy** — Configure health checks and observability via Actuator; validate `/actuator/health` returns `UP`. If health is `DOWN`: check the `components` detail in the response, resolve the failing component (e.g., datasource, broker), and re-validate
17
18## Reference Guide
19
20Load detailed guidance based on context:
21
22| Topic | Reference | Load When |
23|-------|-----------|-----------|
24| Web Layer | `references/web.md` | Controllers, REST APIs, validation, exception handling |
25| Data Access | `references/data.md` | Spring Data JPA, repositories, transactions, projections |
26| Security | `references/security.md` | Spring Security 6, OAuth2, JWT, method security |
27| Cloud Native | `references/cloud.md` | Spring Cloud, Config, Discovery, Gateway, resilience |
28| Testing | `references/testing.md` | @SpringBootTest, MockMvc, Testcontainers, test slices |
29
30## Quick Start — Minimal Working Structure
31
32A standard Spring Boot feature consists of these layers. Use these as copy-paste starting points.
33
34### Entity
35
36```java
37@Entity
38@Table(name = "products")
39public class Product {
40 @Id
41 @GeneratedValue(strategy = GenerationType.IDENTITY)
42 private Long id;
43
44 @NotBlank
45 private String name;
46
47 @DecimalMin("0.0")
48 private BigDecimal price;
49
50 // getters / setters or use @Data (Lombok)
51}
52```
53
54### Repository
55
56```java
57public interface ProductRepository extends JpaRepository<Product, Long> {
58 List<Product> findByNameContainingIgnoreCase(String name);
59}
60```
61
62### Service (constructor injection)
63
64```java
65@Service
66public class ProductService {
67 private final ProductRepository repo;
68
69 public ProductService(ProductRepository repo) { // constructor injection — no @Autowired
70 this.repo = repo;
71 }
72
73 @Transactional(readOnly = true)
74 public List<Product> search(String name) {
75 return repo.findByNameContainingIgnoreCase(name);
76 }
77
78 @Transactional
79 public Product create(ProductRequest request) {
80 var product = new Product();
81 product.setName(request.name());
82 product.setPrice(request.price());
83 return repo.save(product);
84 }
85}
86```
87
88### REST Controller
89
90```java
91@RestController
92@RequestMapping("/api/v1/products")
93@Validated
94public class ProductController {
95 private final ProductService service;
96
97 public ProductController(ProductService service) {
98 this.service = service;
99 }
100
101 @GetMapping
102 public List<Product> search(@RequestParam(defaultValue = "") String name) {
103 return service.search(name);
104 }
105
106 @PostMapping
107 @ResponseStatus(HttpStatus.CREATED)
108 public Product create(@Valid @RequestBody ProductRequest request) {
109 return service.create(request);
110 }
111}
112```
113
114### DTO (record)
115
116```java
117public record ProductRequest(
118 @NotBlank String name,
119 @DecimalMin("0.0") BigDecimal price
120) {}
121```
122
123### Global Exception Handler
124
125```java
126@RestControllerAdvice
127public class GlobalExceptionHandler {
128 @ExceptionHandler(MethodArgumentNotValidException.class)
129 @ResponseStatus(HttpStatus.BAD_REQUEST)
130 public Map<String, String> handleValidation(MethodArgumentNotValidException ex) {
131 return ex.getBindingResult().getFieldErrors().stream()
132 .collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
133 }
134
135 @ExceptionHandler(EntityNotFoundException.class)
136 @ResponseStatus(HttpStatus.NOT_FOUND)
137 public Map<String, String> handleNotFound(EntityNotFoundException ex) {
138 return Map.of("error", ex.getMessage());
139 }
140}
141```
142
143### Test Slice
144
145```java
146@WebMvcTest(ProductController.class)
147class ProductControllerTest {
148 @Autowired MockMvc mockMvc;
149 @MockBean ProductService service;
150
151 @Test
152 void createProduct_validRequest_returns201() throws Exception {
153 var product = new Product(); product.setName("Widget"); product.setPrice(BigDecimal.TEN);
154 when(service.create(any())).thenReturn(product);
155
156 mockMvc.perform(post("/api/v1/products")
157 .contentType(MediaType.APPLICATION_JSON)
158 .content("""{"name":"Widget","price":10.0}"""))
159 .andExpect(status().isCreated())
160 .andExpect(jsonPath("$.name").value("Widget"));
161 }
162}
163```
164
165## Constraints
166
167### MUST DO
168
169| Rule | Correct Pattern |
170|------|----------------|
171| Constructor injection | `public MyService(Dep dep) { this.dep = dep; }` |
172| Validate API input | `@Valid @RequestBody MyRequest req` on every mutating endpoint |
173| Type-safe config | `@ConfigurationProperties(prefix = "app")` bound to a record/class |
174| Appropriate stereotype | `@Service` for business logic, `@Repository` for data, `@RestController` for HTTP |
175| Transaction scope | `@Transactional` on multi-step writes; `@Transactional(readOnly = true)` on reads |
176| Hide internals | Catch domain exceptions in `@RestControllerAdvice`; return problem details, not stack traces |
177| Externalize secrets | Use environment variables or Spring Cloud Config — never `application.properties` |
178
179### MUST NOT DO
180- Use field injection (`@Autowired` on fields)
181- Skip input validation on API endpoints
182- Use `@Component` when `@Service`/`@Repository`/`@Controller` applies
183- Mix blocking and reactive code (e.g., calling `.block()` inside a WebFlux chain)
184- Store secrets or credentials in `application.properties`/`application.yml`
185- Hardcode URLs, credentials, or environment-specific values
186- Use deprecated Spring Boot 2.x patterns (e.g., `WebSecurityConfigurerAdapter`)