@eltonssouza/development-utility-kit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/analyst.md +198 -0
- package/.claude/agents/backend-developer.md +126 -0
- package/.claude/agents/brain-keeper.md +229 -0
- package/.claude/agents/code-reviewer.md +181 -0
- package/.claude/agents/database-engineer.md +94 -0
- package/.claude/agents/devops-engineer.md +141 -0
- package/.claude/agents/frontend-developer.md +97 -0
- package/.claude/agents/gate-keeper.md +118 -0
- package/.claude/agents/migrator.md +291 -0
- package/.claude/agents/mobile-developer.md +80 -0
- package/.claude/agents/n8n-specialist.md +94 -0
- package/.claude/agents/product-owner.md +115 -0
- package/.claude/agents/qa-engineer.md +232 -0
- package/.claude/agents/release-engineer.md +204 -0
- package/.claude/agents/scaffold.md +87 -0
- package/.claude/agents/security-engineer.md +199 -0
- package/.claude/agents/sprint-runner.md +44 -0
- package/.claude/agents/stack-resolver.md +84 -0
- package/.claude/agents/tech-lead.md +182 -0
- package/.claude/agents/update-template.md +54 -0
- package/.claude/agents/ux-designer.md +118 -0
- package/.claude/settings.json +44 -0
- package/.claude/skills/README.md +332 -0
- package/.claude/skills/active-project/SKILL.md +129 -0
- package/.claude/skills/api-integration-test/SKILL.md +64 -0
- package/.claude/skills/auto-test-guard/SKILL.md +237 -0
- package/.claude/skills/auto-test-guard/resources/backend-tests.md +20 -0
- package/.claude/skills/auto-test-guard/resources/e2e-tests.md +24 -0
- package/.claude/skills/auto-test-guard/resources/execution-report.md +49 -0
- package/.claude/skills/auto-test-guard/resources/frontend-tests.md +18 -0
- package/.claude/skills/auto-test-guard/resources/initial-setup.md +108 -0
- package/.claude/skills/auto-test-guard/resources/run-suite.md +48 -0
- package/.claude/skills/auto-test-guard/resources/senior-gate.md +19 -0
- package/.claude/skills/brain-keeper/SKILL.md +60 -0
- package/.claude/skills/brain-keeper/obsidian/app.json +9 -0
- package/.claude/skills/brain-keeper/obsidian/appearance.json +4 -0
- package/.claude/skills/brain-keeper/obsidian/core-plugins.json +20 -0
- package/.claude/skills/brain-keeper/obsidian/daily-notes.json +5 -0
- package/.claude/skills/brain-keeper/obsidian/graph.json +32 -0
- package/.claude/skills/brain-keeper/obsidian/snippets/folder-colors.css +90 -0
- package/.claude/skills/brain-keeper/obsidian/templates.json +5 -0
- package/.claude/skills/brain-keeper/templates/README.md +51 -0
- package/.claude/skills/brain-keeper/templates/adr.md +40 -0
- package/.claude/skills/brain-keeper/templates/bug.md +35 -0
- package/.claude/skills/brain-keeper/templates/daily.md +38 -0
- package/.claude/skills/brain-keeper/templates/feature.md +62 -0
- package/.claude/skills/brain-keeper/templates/meeting.md +34 -0
- package/.claude/skills/brain-keeper/templates/tech-debt.md +21 -0
- package/.claude/skills/caveman/SKILL.md +187 -0
- package/.claude/skills/create-stack-pack/SKILL.md +281 -0
- package/.claude/skills/grill-me/SKILL.md +79 -0
- package/.claude/skills/honcho-memory/SKILL.md +207 -0
- package/.claude/skills/honcho-memory/docs/api-endpoints-verified.md +75 -0
- package/.claude/skills/honcho-memory/hooks/on-prompt-submit.js +221 -0
- package/.claude/skills/honcho-memory/hooks/on-stop.js +193 -0
- package/.claude/skills/honcho-memory/lib/honcho-client.js +363 -0
- package/.claude/skills/honcho-memory/lib/memory-injector.js +93 -0
- package/.claude/skills/honcho-memory/package.json +32 -0
- package/.claude/skills/honcho-memory/scripts/cli.js +370 -0
- package/.claude/skills/honcho-memory/scripts/setup.js +109 -0
- package/.claude/skills/honcho-memory/tests/t001-api-endpoints-verified.test.js +89 -0
- package/.claude/skills/honcho-memory/tests/t002-structure.test.js +97 -0
- package/.claude/skills/honcho-memory/tests/t003-honcho-client.test.js +162 -0
- package/.claude/skills/honcho-memory/tests/t004-soft-delete.test.js +259 -0
- package/.claude/skills/honcho-memory/tests/t005-memory-injector.test.js +175 -0
- package/.claude/skills/honcho-memory/tests/t006-on-prompt-submit.test.js +215 -0
- package/.claude/skills/honcho-memory/tests/t007-on-stop.test.js +165 -0
- package/.claude/skills/honcho-memory/tests/t008-cli.test.js +214 -0
- package/.claude/skills/honcho-memory/tests/t009-setup.test.js +232 -0
- package/.claude/skills/honcho-memory/tests/t010-skill-md.test.js +114 -0
- package/.claude/skills/honcho-memory/tests/t011-settings-hooks.test.js +105 -0
- package/.claude/skills/honcho-memory/tests/t012-docs-update.test.js +106 -0
- package/.claude/skills/honcho-memory/tests/t013-smoke-e2e.test.js +90 -0
- package/.claude/skills/pair-debug/SKILL.md +288 -0
- package/.claude/skills/prd-ready-check/SKILL.md +58 -0
- package/.claude/skills/project-manager/SKILL.md +167 -0
- package/.claude/skills/quality-standards/SKILL.md +201 -0
- package/.claude/skills/quick-feature/SKILL.md +264 -0
- package/.claude/skills/run-sprint/SKILL.md +342 -0
- package/.claude/skills/scaffold/SKILL.md +58 -0
- package/.claude/skills/stack-discovery/SKILL.md +159 -0
- package/.claude/skills/test-coverage-auditor/SKILL.md +59 -0
- package/.claude/skills/to-issues/SKILL.md +163 -0
- package/.claude/skills/to-prd/SKILL.md +130 -0
- package/.claude/skills/update-template/SKILL.md +254 -0
- package/.claude/stacks/CODEOWNERS +30 -0
- package/.claude/stacks/README.md +88 -0
- package/.claude/stacks/_template.md +116 -0
- package/.claude/stacks/java/spring-boot-3.md +376 -0
- package/.claude/stacks/java/spring-boot-4.md +438 -0
- package/.claude/stacks/typescript/angular-18.md +420 -0
- package/.claude/stacks/typescript/angular-19.md +397 -0
- package/.claude/stacks/typescript/angular-21.md +494 -0
- package/CLAUDE.md +453 -0
- package/README.md +391 -0
- package/bin/cli.js +773 -0
- package/bin/lib/backup.js +62 -0
- package/bin/lib/detect-stack.js +476 -0
- package/bin/lib/help.js +233 -0
- package/bin/lib/identity.js +108 -0
- package/bin/lib/local-dir.js +69 -0
- package/bin/lib/manifest.js +236 -0
- package/bin/lib/sync-all.js +394 -0
- package/bin/lib/version-check.js +398 -0
- package/dashboard/db.js +199 -0
- package/dashboard/package.json +22 -0
- package/dashboard/public/app.js +709 -0
- package/dashboard/public/content/docs/agents-reference.en.md +911 -0
- package/dashboard/public/content/docs/architecture-overview.en.md +260 -0
- package/dashboard/public/content/docs/autonomy-matrix.en.md +186 -0
- package/dashboard/public/content/docs/git-flow.en.md +525 -0
- package/dashboard/public/content/docs/honcho-memory.en.md +394 -0
- package/dashboard/public/content/docs/hooks-reference.en.md +420 -0
- package/dashboard/public/content/docs/pipeline.en.md +400 -0
- package/dashboard/public/content/docs/quality-gate.en.md +315 -0
- package/dashboard/public/content/docs/skills-reference.en.md +500 -0
- package/dashboard/public/content/docs/stack-rules.en.md +362 -0
- package/dashboard/public/content/docs/troubleshooting.en.md +637 -0
- package/dashboard/public/content/manifest.json +102 -0
- package/dashboard/public/content/manual/backend.en.md +1138 -0
- package/dashboard/public/content/manual/existing-project.en.md +831 -0
- package/dashboard/public/content/manual/frontend.en.md +1065 -0
- package/dashboard/public/content/manual/fullstack.en.md +1508 -0
- package/dashboard/public/content/manual/mobile.en.md +866 -0
- package/dashboard/public/index.html +108 -0
- package/dashboard/public/style.css +610 -0
- package/dashboard/public/vendor/marked.min.js +69 -0
- package/dashboard/rtk.js +143 -0
- package/dashboard/server-app.js +403 -0
- package/dashboard/server.js +104 -0
- package/dashboard/test/sprint1.test.js +406 -0
- package/dashboard/test/sprint2.test.js +571 -0
- package/dashboard/test/sprint3.test.js +560 -0
- package/package.json +33 -0
- package/scripts/hooks/subagent-telemetry.sh +14 -0
- package/scripts/hooks/telemetry-writer.js +250 -0
- package/scripts/latest-versions.json +56 -0
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
---
|
|
2
|
+
stack: java/spring-boot-4
|
|
3
|
+
versions_covered: "4.0.x"
|
|
4
|
+
last_validated: 2026-05-27
|
|
5
|
+
validated_against: "sandbox — Java 25 LTS + Spring Boot 4.0.0"
|
|
6
|
+
status: active
|
|
7
|
+
pack_owner: "@elton"
|
|
8
|
+
security_review: 2026-05-27
|
|
9
|
+
next_review_due: 2027-05-27
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Java 25 + Spring Boot 4.x
|
|
13
|
+
|
|
14
|
+
Canonical knowledge pack for greenfield projects on Java 25 LTS (or 26) + Spring Boot 4.0+. For projects still on Java 17/21 + Spring Boot 3.x, use `spring-boot-3.md`. SB 4 brings first-class AOT/native compilation, virtual threads by default on several sinks, Spring Security 7, and a maturity step on `@HttpExchange` and `ProblemDetail`.
|
|
15
|
+
|
|
16
|
+
## 1. When to use this pack
|
|
17
|
+
|
|
18
|
+
- Project declares `Primary stack: Java 25 + Spring Boot 4.x` (or Java 26) in `## Project Identity`.
|
|
19
|
+
- `pom.xml` declares `<java.version>25</java.version>` (or 26) and `<spring-boot.version>4.0.x</spring-boot.version>`.
|
|
20
|
+
- Greenfield: **prefer this pack** over `spring-boot-3.md`. SB 4 is the default for new projects since 2026.
|
|
21
|
+
- Legacy maintenance (still on Java 17/21 + SB 3.x): use `spring-boot-3.md` instead.
|
|
22
|
+
|
|
23
|
+
## 2. Stack baseline (what this pack assumes)
|
|
24
|
+
|
|
25
|
+
| Component | Version range | Notes |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| Java | 25 LTS (min) / 26 (latest) | Java 25 LTS until 2033; Virtual Threads + scoped values default; pattern matching for switch maduro; sealed types maduros |
|
|
28
|
+
| Spring Boot | 4.0.x | AOT first-class; native compile sem flags extras; Virtual Threads default em Tomcat + @Scheduled |
|
|
29
|
+
| Jakarta EE | 11 (jakarta.* namespace) | NEVER mix with javax.*; mesmo namespace de SB 3 (sem migration cost) |
|
|
30
|
+
| JPA / Hibernate | Hibernate 7.x (jakarta.persistence) | Records JPA stable; schema validation via Flyway/Liquibase, never Hibernate auto |
|
|
31
|
+
| Spring Security | 7.x | SecurityFilterChain mantido; novo OAuth2 Resource Server DSL mais sucinto |
|
|
32
|
+
| Validation | Jakarta Validation 3.x | `jakarta.validation.constraints.*` |
|
|
33
|
+
| Tomcat / Jetty / Undertow | Tomcat 11 (Servlet 6.1) | Tomcat default; Virtual Threads habilitados via property |
|
|
34
|
+
| Build | Maven 3.9+ or Gradle 8.10+ | Maven Wrapper / Gradle Wrapper committed |
|
|
35
|
+
| Tests | JUnit 5.11+, Mockito 5.14+, Testcontainers 1.21+ | Never H2 as Postgres stand-in |
|
|
36
|
+
| Mutation | PIT (Pitest) 1.16+ | Target ≥70% on domain/ + application/ |
|
|
37
|
+
| Observability | Micrometer Observation API + OpenTelemetry SDK | Tracing W3C Trace Context; HTTP/3 native |
|
|
38
|
+
| HTTP Client | RestClient + `@HttpExchange` declarative interfaces | RestTemplate **deprecated** in SB 4 |
|
|
39
|
+
|
|
40
|
+
## 3. Project structure (DDD)
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
backend/
|
|
44
|
+
├── src/main/java/com/<company>/<project>/
|
|
45
|
+
│ ├── domain/ ← pure POJOs (record-friendly), value objects, domain services, repository PORTS (interfaces)
|
|
46
|
+
│ ├── application/ ← use cases (1 public method each), record DTOs req/res, mappers
|
|
47
|
+
│ ├── infrastructure/ ← JPA adapters, JpaRepository, RestClient + @HttpExchange adapters, security, config
|
|
48
|
+
│ └── web/ ← @RestController, @RestControllerAdvice, filters
|
|
49
|
+
├── src/main/resources/
|
|
50
|
+
│ ├── application.yml
|
|
51
|
+
│ ├── application-{dev,prod}.yml
|
|
52
|
+
│ └── db/migration/V<ts>__<verb>_<subject>.sql ← Flyway migrations
|
|
53
|
+
└── src/test/java/com/<company>/<project>/
|
|
54
|
+
├── <pkg>...Test.java ← unit (JUnit 5 + Mockito)
|
|
55
|
+
└── <pkg>...IT.java ← integration (Testcontainers Postgres 16)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## 4. Code patterns
|
|
59
|
+
|
|
60
|
+
### DTOs as records (req/res separated, Java 25 idioms)
|
|
61
|
+
|
|
62
|
+
```java
|
|
63
|
+
// application/dto/CreateProductRequest.java
|
|
64
|
+
public record CreateProductRequest(
|
|
65
|
+
@NotBlank @Size(max = 120) String name,
|
|
66
|
+
@NotNull @Positive BigDecimal price,
|
|
67
|
+
@NotNull @PositiveOrZero Integer stock
|
|
68
|
+
) {}
|
|
69
|
+
|
|
70
|
+
// application/dto/ProductResponse.java
|
|
71
|
+
public record ProductResponse(
|
|
72
|
+
UUID id, String name, BigDecimal price, Integer stock, Instant createdAt
|
|
73
|
+
) {
|
|
74
|
+
public static ProductResponse from(Product p) {
|
|
75
|
+
return new ProductResponse(p.id(), p.name(), p.price(), p.stock(), p.createdAt());
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Rule**: never expose JPA `@Entity` to controllers. Mapper layer is explicit. Records are the default; use sealed interfaces for domain ADTs (e.g. `sealed interface PaymentResult permits Success, Declined, GatewayError`).
|
|
81
|
+
|
|
82
|
+
### Controller + ProblemDetail (RFC 9457, SB 4 enhancements)
|
|
83
|
+
|
|
84
|
+
```java
|
|
85
|
+
@RestController
|
|
86
|
+
@RequestMapping("/api/v1/products")
|
|
87
|
+
public class ProductController {
|
|
88
|
+
private final CreateProductUseCase createUseCase;
|
|
89
|
+
|
|
90
|
+
public ProductController(CreateProductUseCase createUseCase) {
|
|
91
|
+
this.createUseCase = createUseCase;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@PostMapping
|
|
95
|
+
@ResponseStatus(HttpStatus.CREATED)
|
|
96
|
+
public ProductResponse create(@Valid @RequestBody CreateProductRequest req) {
|
|
97
|
+
return createUseCase.execute(req);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@RestControllerAdvice
|
|
102
|
+
public class GlobalExceptionHandler {
|
|
103
|
+
@ExceptionHandler(MethodArgumentNotValidException.class)
|
|
104
|
+
public ProblemDetail handleValidation(MethodArgumentNotValidException ex) {
|
|
105
|
+
var pd = ProblemDetail.forStatus(HttpStatus.UNPROCESSABLE_ENTITY);
|
|
106
|
+
pd.setTitle("Validation failed");
|
|
107
|
+
// SB 4: MDC correlation ID auto-injected into ProblemDetail "instance"
|
|
108
|
+
pd.setProperty("traceId", MDC.get("traceId"));
|
|
109
|
+
pd.setProperty("errors", ex.getBindingResult().getFieldErrors().stream()
|
|
110
|
+
.map(e -> Map.of("field", e.getField(), "msg", e.getDefaultMessage()))
|
|
111
|
+
.toList());
|
|
112
|
+
return pd;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Pattern matching for switch (Java 25, sealed domain results)
|
|
118
|
+
|
|
119
|
+
```java
|
|
120
|
+
sealed interface PaymentResult permits Success, Declined, GatewayError {}
|
|
121
|
+
record Success(UUID txId) implements PaymentResult {}
|
|
122
|
+
record Declined(String reason) implements PaymentResult {}
|
|
123
|
+
record GatewayError(int status, String body) implements PaymentResult {}
|
|
124
|
+
|
|
125
|
+
// in use case:
|
|
126
|
+
return switch (gateway.charge(cmd)) {
|
|
127
|
+
case Success s -> ProductResponse.confirmed(s.txId());
|
|
128
|
+
case Declined d -> throw new PaymentDeclinedException(d.reason());
|
|
129
|
+
case GatewayError ge -> throw new PaymentGatewayException(ge.status(), ge.body());
|
|
130
|
+
};
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Transactions
|
|
134
|
+
|
|
135
|
+
- `@Transactional(readOnly = true)` on query methods (avoids dirty-check overhead).
|
|
136
|
+
- `@Transactional` on commands. Propagation = REQUIRED (default). Explicit `REQUIRES_NEW` only with documented reason.
|
|
137
|
+
- Never `@Transactional` on controllers — keep on `application/` use cases.
|
|
138
|
+
- SB 4: transactional methods run on virtual threads by default when invoked from a virtual-thread context.
|
|
139
|
+
|
|
140
|
+
### External calls — `@HttpExchange` (declarative, preferred in SB 4)
|
|
141
|
+
|
|
142
|
+
```java
|
|
143
|
+
// infrastructure/adapter/PaymentGatewayClient.java
|
|
144
|
+
@HttpExchange(url = "/charges", accept = "application/json", contentType = "application/json")
|
|
145
|
+
public interface PaymentGatewayClient {
|
|
146
|
+
@PostExchange
|
|
147
|
+
PaymentResult charge(@RequestBody ChargeCommand cmd);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// infrastructure/config/HttpClientConfig.java
|
|
151
|
+
@Configuration
|
|
152
|
+
public class HttpClientConfig {
|
|
153
|
+
@Bean
|
|
154
|
+
PaymentGatewayClient paymentGatewayClient(RestClient.Builder builder, AppProps props) {
|
|
155
|
+
var restClient = builder
|
|
156
|
+
.baseUrl(props.paymentGatewayUrl())
|
|
157
|
+
.requestFactory(ClientHttpRequestFactoryBuilder.detect()
|
|
158
|
+
.build(ClientHttpRequestFactorySettings.defaults()
|
|
159
|
+
.withConnectTimeout(Duration.ofSeconds(3))
|
|
160
|
+
.withReadTimeout(Duration.ofSeconds(10))))
|
|
161
|
+
.build();
|
|
162
|
+
var adapter = RestClientAdapter.create(restClient);
|
|
163
|
+
return HttpServiceProxyFactory.builderFor(adapter).build()
|
|
164
|
+
.createClient(PaymentGatewayClient.class);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Rules**: explicit timeout (connect + read), retry + circuit breaker via Resilience4j on external calls. Map foreign exceptions to domain exceptions. Prefer `@HttpExchange` for typed clients; fall back to `RestClient` directly only when streaming or dynamic URLs.
|
|
170
|
+
|
|
171
|
+
### Virtual Threads (default in SB 4)
|
|
172
|
+
|
|
173
|
+
```yaml
|
|
174
|
+
# application.yml
|
|
175
|
+
spring:
|
|
176
|
+
threads:
|
|
177
|
+
virtual:
|
|
178
|
+
enabled: true # default true in SB 4 — listed here for explicitness
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
```java
|
|
182
|
+
// CompletableFuture on virtual thread executor
|
|
183
|
+
@Bean
|
|
184
|
+
ExecutorService virtualExecutor() {
|
|
185
|
+
return Executors.newVirtualThreadPerTaskExecutor();
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Pagination
|
|
190
|
+
|
|
191
|
+
```java
|
|
192
|
+
@GetMapping
|
|
193
|
+
public Page<ProductResponse> list(Pageable pageable) {
|
|
194
|
+
return service.findAll(pageable).map(ProductResponse::from);
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Client passes `?page=0&size=20&sort=name,asc`. Response wraps `content`, `totalElements`, `totalPages`, `number`, `size`.
|
|
199
|
+
|
|
200
|
+
## 5. Testing
|
|
201
|
+
|
|
202
|
+
### Unit (JUnit 5.11 + Mockito 5.14 + AssertJ)
|
|
203
|
+
|
|
204
|
+
```java
|
|
205
|
+
@ExtendWith(MockitoExtension.class)
|
|
206
|
+
class CreateProductUseCaseTest {
|
|
207
|
+
@Mock ProductRepository repo;
|
|
208
|
+
@InjectMocks CreateProductUseCase useCase;
|
|
209
|
+
|
|
210
|
+
@Test
|
|
211
|
+
void rejectsNegativePrice() {
|
|
212
|
+
var cmd = new CreateProductCommand("widget", BigDecimal.valueOf(-1), 10);
|
|
213
|
+
assertThatThrownBy(() -> useCase.execute(cmd))
|
|
214
|
+
.isInstanceOf(BusinessRuleException.class)
|
|
215
|
+
.hasMessageContaining("price");
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Integration (Testcontainers 1.21, NEVER H2)
|
|
221
|
+
|
|
222
|
+
```java
|
|
223
|
+
@SpringBootTest
|
|
224
|
+
@Testcontainers
|
|
225
|
+
@ActiveProfiles("test")
|
|
226
|
+
class ProductRepositoryIT {
|
|
227
|
+
@Container
|
|
228
|
+
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine");
|
|
229
|
+
|
|
230
|
+
@DynamicPropertySource
|
|
231
|
+
static void props(DynamicPropertyRegistry r) {
|
|
232
|
+
r.add("spring.datasource.url", postgres::getJdbcUrl);
|
|
233
|
+
r.add("spring.datasource.username", postgres::getUsername);
|
|
234
|
+
r.add("spring.datasource.password", postgres::getPassword);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
@Autowired ProductRepository repo;
|
|
238
|
+
// ...
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Web slice (@WebMvcTest, SB 4 supports MockMvcTester fluent API)
|
|
243
|
+
|
|
244
|
+
```java
|
|
245
|
+
@WebMvcTest(ProductController.class)
|
|
246
|
+
class ProductControllerTest {
|
|
247
|
+
@Autowired MockMvcTester mvc; // SB 4 fluent assertions
|
|
248
|
+
@MockitoBean CreateProductUseCase useCase;
|
|
249
|
+
|
|
250
|
+
@Test
|
|
251
|
+
void returns422OnInvalidPayload() {
|
|
252
|
+
assertThat(mvc.post().uri("/api/v1/products")
|
|
253
|
+
.contentType(MediaType.APPLICATION_JSON)
|
|
254
|
+
.content("{}"))
|
|
255
|
+
.hasStatus(HttpStatus.UNPROCESSABLE_ENTITY)
|
|
256
|
+
.bodyJson().extractingPath("$.title").isEqualTo("Validation failed");
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Mutation (PIT 1.16+)
|
|
262
|
+
|
|
263
|
+
Target ≥ 70% on `domain/` + `application/`. Run via:
|
|
264
|
+
```bash
|
|
265
|
+
./mvnw pitest:mutationCoverage
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Native image smoke (optional, AOT first-class in SB 4)
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
./mvnw -Pnative native:compile
|
|
272
|
+
./target/<app> --spring.profiles.active=test
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## 6. Build & run commands
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
./mvnw clean verify # full build + tests + coverage
|
|
279
|
+
./mvnw spring-boot:run # local dev (port 8080), virtual threads on
|
|
280
|
+
./mvnw spring-boot:run -Dspring-boot.run.profiles=dev
|
|
281
|
+
./mvnw spring-boot:test-run # hot-reload (devtools)
|
|
282
|
+
./mvnw native:compile # AOT native image (SB 4 first-class)
|
|
283
|
+
./mvnw spotbugs:check # static analysis
|
|
284
|
+
./mvnw org.owasp:dependency-check-maven:check # CVE scan
|
|
285
|
+
./mvnw pitest:mutationCoverage # mutation testing
|
|
286
|
+
./mvnw jacoco:report # coverage report
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## 7. Security (per ADR-007 + ADR-027 — MANDATORY section)
|
|
290
|
+
|
|
291
|
+
### 7.1 Authentication & Authorization
|
|
292
|
+
|
|
293
|
+
- **Spring Security 7** with `SecurityFilterChain` Bean (NEVER `WebSecurityConfigurerAdapter` — removed since SS 6).
|
|
294
|
+
- **JWT validation** via `spring-boot-starter-oauth2-resource-server`; new SS 7 DSL is more sucinto.
|
|
295
|
+
- **`@PreAuthorize`** on controller methods or use cases. Avoid URL-based authorization for fine-grained rules.
|
|
296
|
+
- **Password hashing**: `BCryptPasswordEncoder(strength = 12)` ou Argon2 quando disponível. NEVER MD5/SHA1.
|
|
297
|
+
|
|
298
|
+
```java
|
|
299
|
+
@Configuration
|
|
300
|
+
@EnableMethodSecurity
|
|
301
|
+
public class SecurityConfig {
|
|
302
|
+
@Bean
|
|
303
|
+
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
|
304
|
+
return http
|
|
305
|
+
.csrf(CsrfConfigurer::disable) // stateless API; enable for stateful UI
|
|
306
|
+
.cors(Customizer.withDefaults())
|
|
307
|
+
.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
|
308
|
+
.authorizeHttpRequests(auth -> auth
|
|
309
|
+
.requestMatchers("/api/v1/auth/**", "/actuator/health").permitAll()
|
|
310
|
+
.anyRequest().authenticated()
|
|
311
|
+
)
|
|
312
|
+
.oauth2ResourceServer(oauth -> oauth.jwt()) // SS 7 sucinto DSL
|
|
313
|
+
.headers(h -> h
|
|
314
|
+
.contentSecurityPolicy(csp -> csp.policyDirectives("default-src 'self'"))
|
|
315
|
+
.httpStrictTransportSecurity(hsts -> hsts.maxAgeInSeconds(31536000).includeSubDomains(true))
|
|
316
|
+
.frameOptions(FrameOptionsConfig::deny)
|
|
317
|
+
)
|
|
318
|
+
.build();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### 7.2 CORS
|
|
324
|
+
|
|
325
|
+
NEVER `*` in production. Explicit list:
|
|
326
|
+
|
|
327
|
+
```java
|
|
328
|
+
@Bean
|
|
329
|
+
public CorsConfigurationSource corsConfigurationSource() {
|
|
330
|
+
var config = new CorsConfiguration();
|
|
331
|
+
config.setAllowedOrigins(List.of("https://app.example.com"));
|
|
332
|
+
config.setAllowedMethods(List.of("GET","POST","PUT","DELETE","OPTIONS"));
|
|
333
|
+
config.setAllowedHeaders(List.of("Authorization","Content-Type"));
|
|
334
|
+
config.setAllowCredentials(true);
|
|
335
|
+
config.setMaxAge(3600L);
|
|
336
|
+
var source = new UrlBasedCorsConfigurationSource();
|
|
337
|
+
source.registerCorsConfiguration("/api/**", config);
|
|
338
|
+
return source;
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### 7.3 Validation & input sanitization
|
|
343
|
+
|
|
344
|
+
- **All `@RequestBody`** must be `@Valid`. ProblemDetail returns 422 on violation.
|
|
345
|
+
- **HTML/XSS**: sanitize via OWASP Java HTML Sanitizer if rendering user input back. Never trust client.
|
|
346
|
+
- **SQL injection**: NEVER concatenate. Use `@Query` with named params, Specifications, or JPA Criteria.
|
|
347
|
+
- **Path traversal**: validate file paths against allowlist; reject `..` sequences; resolve to canonical path before comparison.
|
|
348
|
+
- **Deserialization**: Jackson with `FAIL_ON_UNKNOWN_PROPERTIES=true` for inbound DTOs.
|
|
349
|
+
|
|
350
|
+
### 7.4 Secrets management
|
|
351
|
+
|
|
352
|
+
- NEVER in `application.properties` or `application.yml` committed to git.
|
|
353
|
+
- Use environment variables (`spring.datasource.password=${DB_PASSWORD}`) or Spring Cloud Config + Vault.
|
|
354
|
+
- Local dev: `application-local.yml` in `.gitignore` OR `~/.secrets/<project>.env`.
|
|
355
|
+
- Production: Vault, AWS Secrets Manager, Azure Key Vault, GCP Secret Manager.
|
|
356
|
+
- SB 4: `spring.config.import=optional:vault://...` mais robusto que SB 3.
|
|
357
|
+
|
|
358
|
+
### 7.5 Rate limiting
|
|
359
|
+
|
|
360
|
+
For public endpoints (login, signup, password reset) — Bucket4j 8.x:
|
|
361
|
+
|
|
362
|
+
```java
|
|
363
|
+
@Component
|
|
364
|
+
public class RateLimitFilter extends OncePerRequestFilter {
|
|
365
|
+
private final Bucket bucket = Bucket.builder()
|
|
366
|
+
.addLimit(limit -> limit.capacity(10).refillIntervally(10, Duration.ofMinutes(1)))
|
|
367
|
+
.build();
|
|
368
|
+
// applies on /api/v1/auth/*
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
For distributed rate limiting, prefer Redis-backed Bucket4j (`bucket4j-redis`) or API Gateway (Kong / Spring Cloud Gateway).
|
|
373
|
+
|
|
374
|
+
### 7.6 OWASP Top 10 mapping
|
|
375
|
+
|
|
376
|
+
| OWASP | Mitigation in this stack |
|
|
377
|
+
|---|---|
|
|
378
|
+
| A01 Broken Access Control | `@PreAuthorize` + IDOR check (always validate principal owns resource) |
|
|
379
|
+
| A02 Cryptographic Failures | BCrypt 12 / Argon2; JWT signed RS256 (never HS256 in distributed system); HTTPS/HTTP3 only |
|
|
380
|
+
| A03 Injection | JPA parameterized queries; never `String.format` SQL; input validation |
|
|
381
|
+
| A04 Insecure Design | Threat model + ADR for new bounded contexts; sealed domain types to constrain states |
|
|
382
|
+
| A05 Security Misconfiguration | SecurityFilterChain explicit; headers via `.headers()`; `/actuator/*` restricted; native image reduces attack surface |
|
|
383
|
+
| A06 Vulnerable Components | OWASP dependency-check in CI; Renovate/Dependabot bumps; SBOM via CycloneDX Maven plugin |
|
|
384
|
+
| A07 Auth Failures | Rate limit on auth; `@Lock` on user account after N failures; MFA where critical |
|
|
385
|
+
| A08 Data Integrity | JWT signature; Maven artifact checksums; supply chain via SBOM + Sigstore |
|
|
386
|
+
| A09 Logging Failures | Structured JSON logs + correlation ID via MDC; AUDIT topic separated; PII never logged |
|
|
387
|
+
| A10 SSRF | Allowlist of outbound hosts in `application.yml`; `@HttpExchange` client validates URL against list |
|
|
388
|
+
|
|
389
|
+
### 7.7 LGPD specifics (if applicable)
|
|
390
|
+
|
|
391
|
+
- Identify PII fields (email, CPF, phone) and tag with `@JsonIgnore` on logging serializers.
|
|
392
|
+
- Right to deletion: implement soft-delete + audit log of deletion request.
|
|
393
|
+
- Data subject access: endpoint to export user's data in machine-readable format (JSON/CSV).
|
|
394
|
+
- Encryption at rest for sensitive columns (e.g. via JPA `AttributeConverter` + AES-256-GCM).
|
|
395
|
+
- ANPD breach notification: alarme automático em `security-engineer` quando audit log detecta acesso anômalo.
|
|
396
|
+
|
|
397
|
+
## 8. Anti-patterns (block in code-review)
|
|
398
|
+
|
|
399
|
+
| ❌ Bad | ✅ Good | Why |
|
|
400
|
+
|---|---|---|
|
|
401
|
+
| `@Entity` returned from controller | `@Entity` → mapper → `record Response` | Decouples API contract from DB |
|
|
402
|
+
| `javax.validation.constraints.*` | `jakarta.validation.constraints.*` | SB 3/4 use jakarta namespace |
|
|
403
|
+
| `WebSecurityConfigurerAdapter` | `SecurityFilterChain` Bean | Removed in Spring Security 6+ |
|
|
404
|
+
| `RestTemplate` for new code | `@HttpExchange` or `RestClient` | RestTemplate deprecated in SB 4 |
|
|
405
|
+
| Hibernate 5 patterns (`SessionFactory`) | Hibernate 7 + `EntityManager` | Hibernate 5 incompatible with jakarta |
|
|
406
|
+
| `H2` in tests | `Testcontainers` Postgres 16 | H2 has different SQL dialect |
|
|
407
|
+
| `@Autowired` field injection | Constructor injection | Testable, immutable, final fields |
|
|
408
|
+
| `var` in method signature | Full type in signature | Public API contract should be explicit |
|
|
409
|
+
| `// TODO` committed | `docs/brain/architecture/tech-debt.md` | Trackable |
|
|
410
|
+
| `log.info(token)` | `log.info("token=***")` redaction | Never log credentials/PII |
|
|
411
|
+
| `password.equals(input)` | `passwordEncoder.matches(input, hash)` | Timing-safe + hashed comparison |
|
|
412
|
+
| Platform thread executor for blocking I/O | `Executors.newVirtualThreadPerTaskExecutor()` | Java 25 virtual threads — sem custo de pool |
|
|
413
|
+
| Imperative `if/else` cascading on sealed type | Pattern matching `switch` (Java 25) | Compiler enforces exhaustiveness |
|
|
414
|
+
|
|
415
|
+
## 9. Migration hints — Spring Boot 4.x → 5.x
|
|
416
|
+
|
|
417
|
+
When SB 5 is released (speculative, expected ~2027):
|
|
418
|
+
|
|
419
|
+
- Java baseline likely 26 or 27 LTS. Plan upgrade of Java version first.
|
|
420
|
+
- Spring Security 8: review breaking changes in OAuth2 / WebAuthn DSL.
|
|
421
|
+
- Virtual Threads possibly default em todos os sinks (incluindo JPA).
|
|
422
|
+
- Possible removal of `RestTemplate` (final deprecation).
|
|
423
|
+
- AOT/native compile likely required for production by default.
|
|
424
|
+
- Use `migrator` agent — it tracks both SB 4 and SB 5 packs and proposes ADR per major cross.
|
|
425
|
+
|
|
426
|
+
For migrations **from** SB 3.x **to** SB 4.x, see `spring-boot-3.md` section 9 ("Migration hints").
|
|
427
|
+
|
|
428
|
+
## 10. References
|
|
429
|
+
|
|
430
|
+
- ADR-007 (Senior+ gate thresholds — apply to projects using this pack)
|
|
431
|
+
- ADR-026 (Generic agents + packs architecture)
|
|
432
|
+
- ADR-027 (Pack governance — frontmatter + security review schedule)
|
|
433
|
+
- ADR-029 (Canonical pack format)
|
|
434
|
+
- Spring Boot 4.x release notes: https://github.com/spring-projects/spring-boot/wiki
|
|
435
|
+
- Spring Security 7 reference: https://docs.spring.io/spring-security/reference/
|
|
436
|
+
- Java 25 release notes: https://openjdk.org/projects/jdk/25/
|
|
437
|
+
- OWASP Top 10 (2021): https://owasp.org/Top10/
|
|
438
|
+
- Jakarta EE 11 release notes: https://jakarta.ee/release/11/
|