spring-boot4-skill 1.3.0 → 1.5.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/CHANGELOG.md +22 -0
- package/README.md +27 -8
- package/package.json +1 -1
- package/skills/java-spring-framework/SKILL.md +19 -4
- package/skills/java-spring-framework/references/architecture-patterns.md +156 -0
- package/skills/java-spring-framework/references/microservices-architecture.md +77 -0
- package/skills/java-spring-framework/references/spring-boot-4.md +86 -0
- package/skills/java-spring-framework/references/spring-data-mongodb.md +168 -0
- package/skills/java-spring-framework/references/spring-framework-7.md +37 -0
- package/skills/java-spring-framework/references/spring-graphql.md +150 -0
- package/skills/java-spring-framework/references/spring-modulith.md +11 -0
- package/skills/java-spring-framework/references/spring-redis.md +183 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.5.0] - 2026-02-21
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Skill:** New references `architecture-patterns.md` (hexagonal, Vertical Slice, DDD mapping to Modulith, CQRS) and `microservices-architecture.md` (microservices vs Modulith, service boundaries, inter-service communication, API Gateway, distributed observability).
|
|
13
|
+
- **Skill:** spring-modulith.md new section 10 "DDD & Modulith" (aggregate, domain repository, domain vs application events).
|
|
14
|
+
- **Skill:** SKILL.md and README: reference table and Quick decision mermaid updated for architecture and microservices.
|
|
15
|
+
|
|
16
|
+
[1.5.0]: https://github.com/AyrtonAldayr/agent-skill-java-spring-framework/compare/v1.4.0...v1.5.0
|
|
17
|
+
|
|
18
|
+
## [1.4.0] - 2026-02-21
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **Skill:** New references: `spring-redis.md` (Redis, RedisTemplate, cache backend, session store, pub/sub), `spring-data-mongodb.md` (MongoDB, MongoTemplate, MongoRepository, documents, transactions, indexes), `spring-graphql.md` (Spring for GraphQL, schema, @QueryMapping/@MutationMapping, Records).
|
|
23
|
+
- **Skill:** spring-boot-4.md: new section 14 (API documentation — OpenAPI/springdoc) and section 15 (Scheduling — @Scheduled, cron, virtual threads).
|
|
24
|
+
- **Skill:** spring-framework-7.md: new section 14 (Bean Validation 3.1 — @Valid, @NotNull, @Size, @Email, Records).
|
|
25
|
+
- **Skill:** SKILL.md Reference Files table: Redis, MongoDB, GraphQL rows; extended Load when for spring-boot-4 (OpenAPI/springdoc, scheduling) and spring-framework-7 (Bean Validation, @Valid); Quick decision mermaid nodes for Redis, MongoDB, GraphQL.
|
|
26
|
+
- **README:** Skill reference table: spring-redis.md, spring-data-mongodb.md, spring-graphql.md; extended descriptions for spring-boot-4 and spring-framework-7.
|
|
27
|
+
|
|
28
|
+
[1.4.0]: https://github.com/AyrtonAldayr/agent-skill-java-spring-framework/compare/v1.3.0...v1.4.0
|
|
29
|
+
|
|
8
30
|
## [1.3.0] - 2026-02-21
|
|
9
31
|
|
|
10
32
|
### Added
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/spring-boot4-skill)
|
|
4
4
|
|
|
5
5
|
> **2026-standard** project scaffolder + Claude Code AI skill for Java / Spring development.
|
|
6
|
-
> By [AyrtonAldayr](https://github.com/AyrtonAldayr) · **v1.
|
|
6
|
+
> By [AyrtonAldayr](https://github.com/AyrtonAldayr) · **v1.4.0**
|
|
7
7
|
|
|
8
8
|
This repository provides two tools in one:
|
|
9
9
|
|
|
@@ -133,11 +133,16 @@ Once installed, Claude Code acts as a **Senior Spring Boot 4 architect**:
|
|
|
133
133
|
|
|
134
134
|
| File | Contents |
|
|
135
135
|
|---|---|
|
|
136
|
-
| `skills/java-spring-framework/references/spring-framework-7.md` | All Spring 7 APIs
|
|
137
|
-
| `skills/java-spring-framework/references/spring-boot-4.md` | Boot 4: native, virtual threads, testing
|
|
136
|
+
| `skills/java-spring-framework/references/spring-framework-7.md` | All Spring 7 APIs, Bean Validation, @Valid |
|
|
137
|
+
| `skills/java-spring-framework/references/spring-boot-4.md` | Boot 4: native, virtual threads, testing, reactive stack, rate limiting, connection pools, caching, performance, OpenAPI, scheduling |
|
|
138
138
|
| `skills/java-spring-framework/references/spring-security-7.md` | OAuth2 Resource Server, JWT, method security, CORS |
|
|
139
|
+
| `skills/java-spring-framework/references/spring-redis.md` | Redis, cache distribuido, session store |
|
|
140
|
+
| `skills/java-spring-framework/references/spring-data-mongodb.md` | MongoDB, document DB, Spring Data MongoDB |
|
|
139
141
|
| `skills/java-spring-framework/references/spring-messaging.md` | Kafka, event-driven, @KafkaListener, producer/consumer |
|
|
140
|
-
| `skills/java-spring-framework/references/spring-
|
|
142
|
+
| `skills/java-spring-framework/references/spring-graphql.md` | GraphQL API, Spring for GraphQL |
|
|
143
|
+
| `skills/java-spring-framework/references/spring-modulith.md` | Module structure, events, integration testing, common pitfalls, DDD & Modulith |
|
|
144
|
+
| `skills/java-spring-framework/references/architecture-patterns.md` | DDD, hexagonal, Vertical Slice, CQRS, ports & adapters |
|
|
145
|
+
| `skills/java-spring-framework/references/microservices-architecture.md` | Microservices: boundaries, communication, API Gateway, distributed observability |
|
|
141
146
|
| `skills/java-spring-framework/references/build-templates.md` | Complete Gradle KTS + Maven POM templates |
|
|
142
147
|
| `skills/java-spring-framework/references/troubleshooting-migration.md` | Common errors (javax/jakarta, RestTemplate), Boot 3→4 checklist |
|
|
143
148
|
|
|
@@ -159,10 +164,24 @@ Once installed, Claude Code acts as a **Senior Spring Boot 4 architect**:
|
|
|
159
164
|
|
|
160
165
|
## Publishing to npm
|
|
161
166
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
167
|
+
When releasing a new version:
|
|
168
|
+
|
|
169
|
+
1. **Bump version** in `package.json` (e.g. `1.5.0` → `1.6.0`).
|
|
170
|
+
2. **Update CHANGELOG.md** — add an entry under the new version (Added / Changed / Fixed).
|
|
171
|
+
3. **Commit, tag and push:**
|
|
172
|
+
```bash
|
|
173
|
+
git add .
|
|
174
|
+
git commit -m "chore: release v1.x.0"
|
|
175
|
+
git tag v1.x.0
|
|
176
|
+
git push origin main
|
|
177
|
+
git push origin v1.x.0
|
|
178
|
+
```
|
|
179
|
+
(Or push all tags at once: `git push origin main && git push --tags`.)
|
|
180
|
+
4. **Publish to npm** (requires npm login and 2FA if enabled):
|
|
181
|
+
```bash
|
|
182
|
+
npm login
|
|
183
|
+
npm publish --access public
|
|
184
|
+
```
|
|
166
185
|
|
|
167
186
|
After publishing, developers can use:
|
|
168
187
|
|
package/package.json
CHANGED
|
@@ -29,7 +29,11 @@ idiomatic for **2026 standards**: Spring Boot 4.0.x, Spring Framework 7.0.x, Jav
|
|
|
29
29
|
|
|
30
30
|
```mermaid
|
|
31
31
|
flowchart TD
|
|
32
|
-
A[User request] -->
|
|
32
|
+
A[User request] --> W{Architecture / DDD / hexagonal / VSA?}
|
|
33
|
+
W -->|Yes| X[architecture-patterns.md]
|
|
34
|
+
A --> Y{Microservices design?}
|
|
35
|
+
Y -->|Yes| Z[microservices-architecture.md]
|
|
36
|
+
A --> B{REST blocking or reactive?}
|
|
33
37
|
B -->|Blocking MVC + JDBC/JPA| C[spring-boot-4.md + spring-framework-7.md]
|
|
34
38
|
B -->|Reactive WebFlux + R2DBC| D[spring-boot-4.md Reactive section + spring-framework-7.md]
|
|
35
39
|
A --> E{Modular monolith?}
|
|
@@ -44,6 +48,12 @@ flowchart TD
|
|
|
44
48
|
M -->|Yes| N[spring-messaging.md]
|
|
45
49
|
A --> O{Rate limit / resources / performance?}
|
|
46
50
|
O -->|Yes| P[spring-boot-4.md]
|
|
51
|
+
A --> Q{Redis / cache distribuido?}
|
|
52
|
+
Q -->|Yes| R[spring-redis.md]
|
|
53
|
+
A --> S{MongoDB / document DB?}
|
|
54
|
+
S -->|Yes| T[spring-data-mongodb.md]
|
|
55
|
+
A --> U{GraphQL API?}
|
|
56
|
+
U -->|Yes| V[spring-graphql.md]
|
|
47
57
|
```
|
|
48
58
|
|
|
49
59
|
## Mandatory Workflow
|
|
@@ -80,10 +90,15 @@ Load these as needed — do not load all at once:
|
|
|
80
90
|
|
|
81
91
|
| Topic | File | Load when |
|
|
82
92
|
|---|---|---|
|
|
83
|
-
| Spring Framework 7 APIs | `references/spring-framework-7.md` | Framework-level features: versioning, resilience, JSpecify, SpEL, streaming |
|
|
84
|
-
| Spring Boot 4 features | `references/spring-boot-4.md` | Boot auto-config, Actuator, native images, testing, virtual threads, rate limiting, connection pools, resource metrics, caching, performance tuning |
|
|
93
|
+
| Spring Framework 7 APIs | `references/spring-framework-7.md` | Framework-level features: versioning, resilience, JSpecify, SpEL, streaming, Bean Validation, @Valid |
|
|
94
|
+
| Spring Boot 4 features | `references/spring-boot-4.md` | Boot auto-config, Actuator, native images, testing, virtual threads, rate limiting, connection pools, resource metrics, caching, performance tuning, OpenAPI/springdoc, scheduling |
|
|
85
95
|
| Spring Security 7 | `references/spring-security-7.md` | OAuth2 Resource Server, JWT, method security, CORS, authentication/authorization |
|
|
96
|
+
| Redis | `references/spring-redis.md` | Redis, cache distribuido, session store |
|
|
97
|
+
| MongoDB | `references/spring-data-mongodb.md` | MongoDB, document DB, Spring Data MongoDB |
|
|
86
98
|
| Messaging (Kafka) | `references/spring-messaging.md` | Kafka, event-driven, messaging, @KafkaListener, producer/consumer |
|
|
87
|
-
|
|
|
99
|
+
| GraphQL | `references/spring-graphql.md` | GraphQL API, Spring for GraphQL |
|
|
100
|
+
| Spring Modulith | `references/spring-modulith.md` | Domain-driven module design, event-driven architecture, DDD aggregates, domain repository, domain events |
|
|
101
|
+
| Architecture (DDD, hexagonal, VSA, CQRS) | `references/architecture-patterns.md` | DDD, hexagonal, ports & adapters, Vertical Slice, CQRS, bounded context mapping |
|
|
102
|
+
| Microservices architecture | `references/microservices-architecture.md` | Microservices design, service boundaries, inter-service communication, API Gateway, distributed tracing |
|
|
88
103
|
| Build templates | `references/build-templates.md` | Gradle KTS or Maven POM scaffolding with 2026 BOM versions |
|
|
89
104
|
| Troubleshooting & migration | `references/troubleshooting-migration.md` | Migration from Boot 3, compile/runtime errors (javax/jakarta, RestTemplate, native, null-safety) |
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Application Architecture Patterns — Spring Boot 4
|
|
2
|
+
|
|
3
|
+
**Spring Boot**: 4.0.x | Application architecture | **Jakarta EE**: 11
|
|
4
|
+
|
|
5
|
+
This reference covers hexagonal (ports & adapters), Vertical Slice Architecture, DDD mapping to Modulith, and optional CQRS. For module structure and event-driven communication, see `references/spring-modulith.md`. For Kafka messaging, see `references/spring-messaging.md`.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
1. [Hexagonal (Ports & Adapters) with Spring](#1-hexagonal-ports--adapters-with-spring)
|
|
12
|
+
2. [Vertical Slice Architecture](#2-vertical-slice-architecture)
|
|
13
|
+
3. [DDD Mapping to Modulith](#3-ddd-mapping-to-modulith)
|
|
14
|
+
4. [CQRS (Optional)](#4-cqrs-optional)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. Hexagonal (Ports & Adapters) with Spring
|
|
19
|
+
|
|
20
|
+
**Ports** are interfaces that define what the application needs (inbound: use cases; outbound: persistence, messaging, external APIs). **Adapters** are concrete implementations that plug into those ports: REST controllers, JPA/JdbcClient repositories, Kafka listeners.
|
|
21
|
+
|
|
22
|
+
The domain and use cases stay in the center with no framework dependencies; Spring only appears in adapter classes and `@Bean` configuration.
|
|
23
|
+
|
|
24
|
+
**Conceptual layout:**
|
|
25
|
+
|
|
26
|
+
```mermaid
|
|
27
|
+
flowchart LR
|
|
28
|
+
subgraph adaptersIn [Driving Adapters]
|
|
29
|
+
REST[REST Controller]
|
|
30
|
+
end
|
|
31
|
+
subgraph ports [Ports]
|
|
32
|
+
UseCase[PlaceOrderPort]
|
|
33
|
+
end
|
|
34
|
+
subgraph domain [Domain]
|
|
35
|
+
Order[Order aggregate]
|
|
36
|
+
end
|
|
37
|
+
subgraph adaptersOut [Driven Adapters]
|
|
38
|
+
Repo[OrderRepository impl]
|
|
39
|
+
Events[EventPublisher]
|
|
40
|
+
end
|
|
41
|
+
REST --> UseCase
|
|
42
|
+
UseCase --> Order
|
|
43
|
+
UseCase --> Repo
|
|
44
|
+
UseCase --> Events
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Port (interface):**
|
|
48
|
+
|
|
49
|
+
```java
|
|
50
|
+
package com.example.shop.orders.application;
|
|
51
|
+
|
|
52
|
+
import com.example.shop.orders.domain.Order;
|
|
53
|
+
|
|
54
|
+
public interface PlaceOrderPort {
|
|
55
|
+
Order placeOrder(PlaceOrderCommand command);
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Use case (implements or uses the port from the “inside”):**
|
|
60
|
+
|
|
61
|
+
```java
|
|
62
|
+
package com.example.shop.orders.application;
|
|
63
|
+
|
|
64
|
+
import com.example.shop.orders.domain.Order;
|
|
65
|
+
import org.springframework.stereotype.Service;
|
|
66
|
+
|
|
67
|
+
@Service
|
|
68
|
+
public class PlaceOrderUseCase implements PlaceOrderPort {
|
|
69
|
+
|
|
70
|
+
private final OrderRepository orderRepository;
|
|
71
|
+
private final ApplicationEventPublisher events;
|
|
72
|
+
|
|
73
|
+
public PlaceOrderUseCase(OrderRepository orderRepository, ApplicationEventPublisher events) {
|
|
74
|
+
this.orderRepository = orderRepository;
|
|
75
|
+
this.events = events;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@Override
|
|
79
|
+
public Order placeOrder(PlaceOrderCommand command) {
|
|
80
|
+
Order order = Order.create(command.sku(), command.quantity());
|
|
81
|
+
orderRepository.save(order);
|
|
82
|
+
events.publishEvent(new OrderPlacedEvent(order.id(), order.sku(), order.quantity()));
|
|
83
|
+
return order;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Driven port (repository):**
|
|
89
|
+
|
|
90
|
+
```java
|
|
91
|
+
package com.example.shop.orders.domain;
|
|
92
|
+
|
|
93
|
+
public interface OrderRepository {
|
|
94
|
+
Order save(Order order);
|
|
95
|
+
Order findById(Long id);
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Adapter (REST):** The controller depends on the port (e.g. `PlaceOrderPort`) and delegates; the adapter is registered as a `@Bean` or the port is injected where the controller is defined. **Adapter (persistence):** A class in `internal/` implements `OrderRepository` using JdbcClient or JPA and is registered as a `@Bean`. **Adapter (messaging):** Outbound events can be published via `ApplicationEventPublisher` (in-process) or Kafka (see `references/spring-messaging.md`).
|
|
100
|
+
|
|
101
|
+
Each Modulith module can apply hexagonal internally: public API = driving ports (use cases); `internal/` = driven adapters (repositories, external clients). See `references/spring-modulith.md` for module package structure.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 2. Vertical Slice Architecture
|
|
106
|
+
|
|
107
|
+
Organize by **feature / use case** instead of horizontal layers (controllers/, services/, repositories/). Each **vertical slice** contains everything needed for one capability: HTTP handler, application logic, persistence, and DTOs.
|
|
108
|
+
|
|
109
|
+
**Package layout example (within an `orders` module):**
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
com.example.shop.orders/
|
|
113
|
+
├── PlaceOrder/
|
|
114
|
+
│ ├── PlaceOrderController.java # REST endpoint
|
|
115
|
+
│ ├── PlaceOrderCommand.java # request DTO
|
|
116
|
+
│ ├── PlaceOrderHandler.java # use case
|
|
117
|
+
│ └── OrderRepository.java # port; impl in internal or same slice
|
|
118
|
+
├── GetOrder/
|
|
119
|
+
│ ├── GetOrderController.java
|
|
120
|
+
│ ├── GetOrderHandler.java
|
|
121
|
+
│ └── ...
|
|
122
|
+
└── internal/
|
|
123
|
+
└── JdbcOrderRepository.java # adapter shared by slices if needed
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Each slice is a thin vertical: from HTTP (or message) down to persistence. Shared infrastructure (e.g. repository implementation) can live in `internal/` or a shared package. With **Modulith**, the module boundary is the root package (e.g. `orders/`); inside it you can structure by slices (PlaceOrder, GetOrder) or by role (OrderService, OrderController) — both are valid. Use Modulith’s verification and event-driven rules regardless; see `references/spring-modulith.md`.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 3. DDD Mapping to Modulith
|
|
131
|
+
|
|
132
|
+
| DDD concept | Modulith mapping |
|
|
133
|
+
|-------------|------------------|
|
|
134
|
+
| **Bounded context** | One root package = one module (e.g. `orders/`, `inventory/`). |
|
|
135
|
+
| **Use case / application service** | Public service class in the module (e.g. `OrderService`) or a dedicated use-case class that the controller calls. |
|
|
136
|
+
| **Aggregate** | Domain entity (or group of entities) that the module’s service loads and persists as a unit. See section 10 “DDD & Modulith” in `references/spring-modulith.md` for aggregate and domain repository. |
|
|
137
|
+
| **Domain repository** | Interface in the module’s public or domain package; implementation in `internal/` (JdbcClient/JPA). Same as a “port” in hexagonal. |
|
|
138
|
+
| **Domain events** | Often represented as application events published via `ApplicationEventPublisher` and consumed by other modules with `@ApplicationModuleListener`. See “DDD & Modulith” in `references/spring-modulith.md` for domain vs application events. |
|
|
139
|
+
|
|
140
|
+
You do not need a separate “domain” package; the public API of the module can expose use cases and domain types (e.g. Order record). Keep infrastructure (repositories impl, HTTP clients) in `internal/` or in adapters.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## 4. CQRS (Optional)
|
|
145
|
+
|
|
146
|
+
**CQRS** separates the **write model** (commands, aggregates, transactional consistency) from the **read model** (projections, queries, possibly different storage).
|
|
147
|
+
|
|
148
|
+
**In a modular monolith (Modulith):** The module that owns the aggregate handles commands and publishes events (see transactional event listeners in `references/spring-modulith.md`). The same or another module can subscribe to those events and update a read model (e.g. a dedicated table or document store). Queries are served from the read model.
|
|
149
|
+
|
|
150
|
+
**Across services:** Use Kafka (or another message broker) to publish domain events; consumer services build and maintain their own read models. See `references/spring-messaging.md` for producers and consumers. Ensure idempotency and ordering guarantees in consumers when building read models.
|
|
151
|
+
|
|
152
|
+
Consider CQRS when read and write workloads or models diverge significantly (e.g. many reads with different shapes, or event-sourced writes with separate projections). For simple CRUD, a single model is often enough.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
**Summary:** Use **ports (interfaces)** for use cases and repositories; implement them with **adapters** (REST, JPA/JdbcClient, Kafka). Structure by **Vertical Slice** per feature if it fits your team. Map **DDD** bounded contexts to **Modulith** modules and use the “DDD & Modulith” section in `references/spring-modulith.md` for aggregates and domain events. Use **CQRS** with event-driven updates when read and write models need to diverge; combine Modulith events and Kafka as needed.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Microservices Architecture — Spring Boot 4
|
|
2
|
+
|
|
3
|
+
**Spring Boot**: 4.0.x | Microservices design | **Jakarta EE**: 11
|
|
4
|
+
|
|
5
|
+
This reference covers when to choose microservices vs a modular monolith, service boundaries, inter-service communication, API Gateway, and distributed observability. For building each service, use `references/spring-boot-4.md`, `references/spring-framework-7.md`, and for event-driven messaging `references/spring-messaging.md`. For a modular monolith alternative, see `references/spring-modulith.md`.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
1. [When to Choose Microservices vs Modular Monolith (Modulith)](#1-when-to-choose-microservices-vs-modular-monolith-modulith)
|
|
12
|
+
2. [Service Boundaries](#2-service-boundaries)
|
|
13
|
+
3. [Inter-Service Communication](#3-inter-service-communication)
|
|
14
|
+
4. [API Gateway](#4-api-gateway)
|
|
15
|
+
5. [Distributed Observability](#5-distributed-observability)
|
|
16
|
+
6. [Config and Discovery (Optional)](#6-config-and-discovery-optional)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 1. When to Choose Microservices vs Modular Monolith (Modulith)
|
|
21
|
+
|
|
22
|
+
| Factor | Prefer Modulith (modular monolith) | Prefer Microservices |
|
|
23
|
+
|--------|-------------------------------------|----------------------|
|
|
24
|
+
| **Team structure** | Single team or few teams; shared codebase is manageable | Multiple teams owning different services; need independent delivery |
|
|
25
|
+
| **Scaling** | Scale the whole application (e.g. more replicas of the same process) | Scale individual services (e.g. only the heavy-read service) |
|
|
26
|
+
| **Deployment** | One deployable; simpler pipelines and rollbacks | Independent deploy per service; more operational complexity |
|
|
27
|
+
| **Transactions** | Single database or local transactions across modules | Distributed transactions are hard; prefer eventual consistency and events |
|
|
28
|
+
| **Latency** | In-process calls; low latency between modules | Network calls between services; higher latency and failure modes |
|
|
29
|
+
| **Domain** | Domains are related; modules can communicate via events in one process | Bounded contexts are clearly separate (e.g. different companies or products) |
|
|
30
|
+
|
|
31
|
+
Start with a **modular monolith** (Modulith) when the domain and team do not clearly demand separate deployments and scaling. Extract to microservices when you hit real limits (team boundaries, scaling, technology diversity). See `references/spring-modulith.md` for module structure and event-driven communication inside one application.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 2. Service Boundaries
|
|
36
|
+
|
|
37
|
+
- **One service = one bounded context** (or a cohesive subdomain). Avoid sharing a single database across services; each service typically owns its own data store. This reduces coupling and allows independent schema evolution.
|
|
38
|
+
- **API contracts:** Define clear contracts between services. For REST, use **OpenAPI**; generate client DTOs or use a shared library. Document and version the API (e.g. URL path or header). See the OpenAPI/springdoc section in `references/spring-boot-4.md` for exposing and documenting REST APIs from each service.
|
|
39
|
+
- **Database per service:** Each service uses its own database (or schema). Replicate data across services only via events or explicit sync APIs when necessary; avoid distributed transactions.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 3. Inter-Service Communication
|
|
44
|
+
|
|
45
|
+
**Synchronous (request/response):** Use **RestClient** (see `references/spring-framework-7.md`) to call other services. Share contracts (OpenAPI spec or DTOs). Handle timeouts, retries, and circuit breakers; Spring 7’s resilience annotations (`@Retryable`, `@CircuitBreaker`) can wrap outbound calls. For high-throughput or low-latency sync, consider gRPC in addition to REST.
|
|
46
|
+
|
|
47
|
+
**Asynchronous (events):** Use **Kafka** (or similar) for cross-service events. The producer service publishes domain events; consumer services subscribe and update their own state. Prefer events when you need decoupling, resilience (consumer can process later), or audit. See `references/spring-messaging.md` for producers and `@KafkaListener` consumers. Design for **idempotency** (consumer can process the same message twice safely) and **ordering** (partition by key when order matters).
|
|
48
|
+
|
|
49
|
+
**When to use which:** Use sync when the caller needs an immediate response (e.g. “get order by ID”). Use async when the operation can complete later or when multiple services react to the same event (e.g. “order placed” → inventory, notifications, analytics).
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 4. API Gateway
|
|
54
|
+
|
|
55
|
+
An **API Gateway** sits in front of your services and handles routing, authentication/authorization at the edge, rate limiting, and sometimes aggregation. **Spring Cloud Gateway** is the Spring-based option: you configure routes (e.g. by path or host), filters (JWT validation, rate limiting), and point to backend services. Rate limiting at the gateway was mentioned in `references/spring-boot-4.md` (Rate limiting) as an alternative to in-app Bucket4j.
|
|
56
|
+
|
|
57
|
+
Detailed route and filter configuration is outside this skill; see the [Spring Cloud Gateway documentation](https://docs.spring.io/spring-cloud-gateway/reference/) for setup. Each backend service remains a Spring Boot application with its own security (e.g. OAuth2 Resource Server) as in `references/spring-security-7.md`.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 5. Distributed Observability
|
|
62
|
+
|
|
63
|
+
**Distributed tracing:** Propagate a **trace ID** across service boundaries so a single request can be followed through multiple services. **OpenTelemetry** (OTEL) with W3C Trace Context headers is the standard. Each Spring Boot 4 service enables Actuator and OTEL as in `references/spring-boot-4.md` (Actuator & Observability). Ensure the HTTP client (RestClient) and Kafka producers/consumers propagate trace context; Boot’s OTEL integration typically does this when configured. The collector receives spans from all services and correlates them by trace ID.
|
|
64
|
+
|
|
65
|
+
**Metrics and logs:** Each service exposes metrics (e.g. Prometheus) and logs in a consistent format. Aggregate metrics and logs in a central platform (e.g. Grafana, centralized logging). Use the same stack (Micrometer, OTEL) in every service for consistency. See `references/spring-boot-4.md` for Actuator, OTEL export, and health groups.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 6. Config and Discovery (Optional)
|
|
70
|
+
|
|
71
|
+
**Centralized config:** **Spring Cloud Config** can provide configuration (e.g. `application.yaml`) to multiple services from a central server. Use it when you need to change config without redeploying each service. See the [Spring Cloud Config documentation](https://docs.spring.io/spring-cloud-config/reference/) for setup.
|
|
72
|
+
|
|
73
|
+
**Service discovery:** When services need to find each other by name (e.g. “order-service” instead of a fixed URL), use a discovery mechanism (e.g. Consul, Eureka, or Kubernetes services). Spring Cloud supports discovery clients; each service registers itself and resolves others by name. This is optional; you can also use static URLs or a gateway that routes by path.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
**Summary:** Choose **microservices** when team boundaries, independent scaling, or deployment justify the operational cost; otherwise start with a **modular monolith** (Modulith). Define **service boundaries** around bounded contexts and avoid shared databases. Use **RestClient** for sync and **Kafka** for async communication; design for idempotency and ordering. Put an **API Gateway** (e.g. Spring Cloud Gateway) at the edge for routing and auth. Use **OpenTelemetry** and propagate trace context so you get **distributed observability** across all services.
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
11. [Reactive Stack (R2DBC + WebFlux)](#11-reactive-stack-r2dbc--webflux)
|
|
19
19
|
12. [Rate Limiting](#12-rate-limiting)
|
|
20
20
|
13. [Resources & Performance](#13-resources--performance)
|
|
21
|
+
14. [API Documentation (OpenAPI / springdoc)](#14-api-documentation-openapi--springdoc)
|
|
22
|
+
15. [Scheduling](#15-scheduling)
|
|
21
23
|
|
|
22
24
|
---
|
|
23
25
|
|
|
@@ -598,3 +600,87 @@ Use short TTLs or size limits to avoid stale or unbounded caches.
|
|
|
598
600
|
- **Pool sizing:** Set HikariCP (or R2DBC pool) size based on observed metrics (connections in use, pending); avoid over-provisioning.
|
|
599
601
|
- **N+1:** Avoid N+1 queries in JPA (e.g. `@EntityGraph`, fetch joins, or DTO projections) so a single request does not open many statements.
|
|
600
602
|
- **Observability:** Use Actuator/Prometheus and traces (section 4) to find bottlenecks (slow endpoints, DB, or external calls) and tune accordingly. No JVM-level tuning (heap, GC) is covered here.
|
|
603
|
+
|
|
604
|
+
---
|
|
605
|
+
|
|
606
|
+
## 14. API Documentation (OpenAPI / springdoc)
|
|
607
|
+
|
|
608
|
+
Use **springdoc-openapi** to expose OpenAPI 3 spec and Swagger UI from your REST controllers. The JSON/YAML spec is consumed by clients and code generators; the UI is useful in development.
|
|
609
|
+
|
|
610
|
+
**Dependency:**
|
|
611
|
+
|
|
612
|
+
```kotlin
|
|
613
|
+
// build.gradle.kts
|
|
614
|
+
dependencies {
|
|
615
|
+
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0") // align with Boot 4
|
|
616
|
+
}
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
**application.yaml:**
|
|
620
|
+
|
|
621
|
+
```yaml
|
|
622
|
+
springdoc:
|
|
623
|
+
api-docs:
|
|
624
|
+
path: /v3/api-docs
|
|
625
|
+
swagger-ui:
|
|
626
|
+
path: /swagger-ui.html
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
Document controllers with `@Operation` and `@Parameter`:
|
|
630
|
+
|
|
631
|
+
```java
|
|
632
|
+
import io.swagger.v3.oas.annotations.Operation;
|
|
633
|
+
import io.swagger.v3.oas.annotations.Parameter;
|
|
634
|
+
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
635
|
+
|
|
636
|
+
@RestController
|
|
637
|
+
@RequestMapping("/api/products")
|
|
638
|
+
@Tag(name = "Products", description = "Product API")
|
|
639
|
+
public class ProductController {
|
|
640
|
+
|
|
641
|
+
@GetMapping("/{id}")
|
|
642
|
+
@Operation(summary = "Get product by ID")
|
|
643
|
+
public Product get(@Parameter(description = "Product ID") @PathVariable String id) {
|
|
644
|
+
return productService.findById(id);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
The OpenAPI spec is available at `springdoc.api-docs.path` (default `/v3/api-docs`); Swagger UI at `springdoc.swagger-ui.path`. Protect these endpoints in production via Spring Security 7 (e.g. allow only for authenticated users or internal network). See [springdoc documentation](https://springdoc.org/) for grouping, security schemes, and more.
|
|
650
|
+
|
|
651
|
+
---
|
|
652
|
+
|
|
653
|
+
## 15. Scheduling
|
|
654
|
+
|
|
655
|
+
Use `@EnableScheduling` and `@Scheduled` for periodic tasks (cron, fixed rate, or fixed delay).
|
|
656
|
+
|
|
657
|
+
**Enable scheduling:**
|
|
658
|
+
|
|
659
|
+
```java
|
|
660
|
+
@SpringBootApplication
|
|
661
|
+
@EnableScheduling
|
|
662
|
+
public class Application { ... }
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
**Scheduled component:**
|
|
666
|
+
|
|
667
|
+
```java
|
|
668
|
+
import org.springframework.scheduling.annotation.Scheduled;
|
|
669
|
+
import org.springframework.stereotype.Component;
|
|
670
|
+
|
|
671
|
+
@Component
|
|
672
|
+
public class SyncJob {
|
|
673
|
+
|
|
674
|
+
@Scheduled(fixedRate = 60000) // every 60 seconds
|
|
675
|
+
public void sync() {
|
|
676
|
+
// run task
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
@Scheduled(cron = "0 0 * * * *") // every hour at minute 0
|
|
680
|
+
public void hourlyReport() {
|
|
681
|
+
// run task
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
With **virtual threads** enabled (section 6), scheduled tasks run on virtual threads when the default task scheduler is used; blocking work in the task does not block platform threads. For a custom `TaskScheduler` (e.g. thread pool size), define a `TaskScheduler` bean and configure as needed. Spring Batch is not covered here; for batch jobs (chunk-based reading/writing), see Spring Batch documentation.
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Spring Data MongoDB — Boot 4
|
|
2
|
+
|
|
3
|
+
**Spring Boot**: 4.0.x | **Spring Data MongoDB**: aligned with Boot BOM | **Jakarta EE**: 11
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Dependencies](#1-dependencies)
|
|
10
|
+
2. [application.yaml](#2-applicationyaml)
|
|
11
|
+
3. [Documents and @Document](#3-documents-and-document)
|
|
12
|
+
4. [MongoRepository](#4-mongorepository)
|
|
13
|
+
5. [MongoTemplate](#5-mongotemplate)
|
|
14
|
+
6. [Transactions and indexes](#6-transactions-and-indexes)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. Dependencies
|
|
19
|
+
|
|
20
|
+
Spring Boot 4 BOM manages Spring Data MongoDB. Add the starter:
|
|
21
|
+
|
|
22
|
+
```kotlin
|
|
23
|
+
// build.gradle.kts
|
|
24
|
+
dependencies {
|
|
25
|
+
implementation("org.springframework.boot:spring-boot-starter-data-mongodb")
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 2. application.yaml
|
|
32
|
+
|
|
33
|
+
```yaml
|
|
34
|
+
spring:
|
|
35
|
+
data:
|
|
36
|
+
mongodb:
|
|
37
|
+
uri: mongodb://localhost:27017/mydb
|
|
38
|
+
# or separately:
|
|
39
|
+
# host: localhost
|
|
40
|
+
# port: 27017
|
|
41
|
+
# database: mydb
|
|
42
|
+
# username: app
|
|
43
|
+
# password: secret
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
For connection pool tuning (MongoDB driver):
|
|
47
|
+
|
|
48
|
+
```yaml
|
|
49
|
+
spring:
|
|
50
|
+
data:
|
|
51
|
+
mongodb:
|
|
52
|
+
uri: mongodb://localhost:27017/mydb
|
|
53
|
+
auto-index-creation: true
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 3. Documents and @Document
|
|
59
|
+
|
|
60
|
+
Use a Java record or class with `@Document` and `@Id`. Prefer records for DTOs; for entities that need lazy loading or complex mapping, a class is fine.
|
|
61
|
+
|
|
62
|
+
```java
|
|
63
|
+
import org.springframework.data.annotation.Id;
|
|
64
|
+
import org.springframework.data.mongodb.core.mapping.Document;
|
|
65
|
+
import org.springframework.data.mongodb.core.index.Indexed;
|
|
66
|
+
|
|
67
|
+
@Document(collection = "products")
|
|
68
|
+
public record Product(
|
|
69
|
+
@Id String id,
|
|
70
|
+
String name,
|
|
71
|
+
@Indexed String sku,
|
|
72
|
+
java.math.BigDecimal price
|
|
73
|
+
) {}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
For mutable entities (e.g. append-only fields), use a class with getters/setters and `@Id` on the identifier field.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 4. MongoRepository
|
|
81
|
+
|
|
82
|
+
Extend `MongoRepository<Entity, IdType>` for CRUD and query methods. Use `String` as ID type for MongoDB ObjectId-backed ids.
|
|
83
|
+
|
|
84
|
+
```java
|
|
85
|
+
import org.springframework.data.mongodb.repository.MongoRepository;
|
|
86
|
+
import java.util.List;
|
|
87
|
+
|
|
88
|
+
public interface ProductRepository extends MongoRepository<Product, String> {
|
|
89
|
+
|
|
90
|
+
List<Product> findByName(String name);
|
|
91
|
+
|
|
92
|
+
List<Product> findByPriceBetween(java.math.BigDecimal min, java.math.BigDecimal max);
|
|
93
|
+
|
|
94
|
+
boolean existsBySku(String sku);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Custom queries with `@Query`:
|
|
99
|
+
|
|
100
|
+
```java
|
|
101
|
+
@Query("{ 'name' : { $regex: ?0, $options: 'i' } }")
|
|
102
|
+
List<Product> findByNameRegex(String pattern);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Use `MongoRepository` when you need standard CRUD and derived queries; use `MongoTemplate` for dynamic queries or aggregations (see [Spring Data MongoDB docs](https://docs.spring.io/spring-data/mongodb/reference/) for aggregation pipelines).
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 5. MongoTemplate
|
|
110
|
+
|
|
111
|
+
For programmatic or dynamic queries, inject `MongoTemplate`:
|
|
112
|
+
|
|
113
|
+
```java
|
|
114
|
+
import org.springframework.data.mongodb.core.MongoTemplate;
|
|
115
|
+
import org.springframework.data.mongodb.core.query.Criteria;
|
|
116
|
+
import org.springframework.data.mongodb.core.query.Query;
|
|
117
|
+
import org.springframework.data.mongodb.core.query.Update;
|
|
118
|
+
import org.springframework.stereotype.Service;
|
|
119
|
+
|
|
120
|
+
@Service
|
|
121
|
+
public class ProductService {
|
|
122
|
+
|
|
123
|
+
private final MongoTemplate mongo;
|
|
124
|
+
|
|
125
|
+
public ProductService(MongoTemplate mongo) {
|
|
126
|
+
this.mongo = mongo;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public Product findById(String id) {
|
|
130
|
+
return mongo.findById(id, Product.class);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public List<Product> findBySku(String sku) {
|
|
134
|
+
return mongo.find(Query.query(Criteria.where("sku").is(sku)), Product.class);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public Product insert(Product product) {
|
|
138
|
+
return mongo.insert(product);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public void updatePrice(String id, java.math.BigDecimal price) {
|
|
142
|
+
mongo.updateFirst(
|
|
143
|
+
Query.query(Criteria.where("id").is(id)),
|
|
144
|
+
Update.update("price", price),
|
|
145
|
+
Product.class
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 6. Transactions and indexes
|
|
154
|
+
|
|
155
|
+
**Transactions:** Supported when MongoDB is run as a replica set. Enable with `@Transactional` on the method (or class). Spring Data MongoDB uses the same transaction manager as other Spring Data stores when configured.
|
|
156
|
+
|
|
157
|
+
```java
|
|
158
|
+
@Transactional
|
|
159
|
+
public void transfer(Product from, Product to, int qty) {
|
|
160
|
+
// multiple read/write operations in one transaction
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Indexes:** Use `@Indexed` on fields for single-field indexes. For compound or custom indexes, create them at startup via `MongoTemplate.indexOps(Product.class).ensureIndex(new Index().on("name", Sort.Direction.ASC).on("price", Sort.Direction.DESC))` or with `@CompoundIndex` on the document class. Set `spring.data.mongodb.auto-index-creation: true` in development; in production, manage indexes via migrations or infrastructure as code.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
**Summary:** Use `spring-boot-starter-data-mongodb` with Boot 4 BOM, configure URI (or host/port/database) in `application.yaml`, and model documents with `@Document` and `@Id`. Use `MongoRepository` for CRUD and derived queries; use `MongoTemplate` for dynamic or programmatic access. Use transactions on replica sets and define indexes via `@Indexed` or programmatic API. For aggregation pipelines, see the official Spring Data MongoDB reference.
|
|
@@ -20,6 +20,7 @@ For **reactive applications (WebFlux + R2DBC)**, combine this reference with the
|
|
|
20
20
|
11. [Enhanced PathPattern](#11-enhanced-pathpattern)
|
|
21
21
|
12. [RestTestClient (Testing)](#12-resttestclient-testing)
|
|
22
22
|
13. [Removed APIs Migration](#13-removed-apis-migration)
|
|
23
|
+
14. [Bean Validation 3.1](#14-bean-validation-31)
|
|
23
24
|
|
|
24
25
|
---
|
|
25
26
|
|
|
@@ -316,3 +317,39 @@ class ProductControllerTest {
|
|
|
316
317
|
| XML Spring MVC config | `WebMvcConfigurer` (Java) |
|
|
317
318
|
| `suffixPatternMatch` | Explicit media types |
|
|
318
319
|
| `trailingSlashMatch` | Explicit URI templates |
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## 14. Bean Validation 3.1
|
|
324
|
+
|
|
325
|
+
Spring MVC and WebFlux support **Jakarta Bean Validation 3.1** (Boot 4 brings the dependency). Use `@Valid` (or `@Validated`) on `@RequestBody`, and optionally on `@RequestParam` / path variables with group validation or custom validators.
|
|
326
|
+
|
|
327
|
+
**DTO with constraints:**
|
|
328
|
+
|
|
329
|
+
```java
|
|
330
|
+
import jakarta.validation.constraints.Email;
|
|
331
|
+
import jakarta.validation.constraints.NotBlank;
|
|
332
|
+
import jakarta.validation.constraints.Size;
|
|
333
|
+
|
|
334
|
+
public record CreateUserRequest(
|
|
335
|
+
@NotBlank(message = "Name is required")
|
|
336
|
+
@Size(min = 1, max = 100)
|
|
337
|
+
String name,
|
|
338
|
+
|
|
339
|
+
@NotBlank
|
|
340
|
+
@Email(message = "Must be a valid email")
|
|
341
|
+
String email
|
|
342
|
+
) {}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
**Controller:**
|
|
346
|
+
|
|
347
|
+
```java
|
|
348
|
+
@PostMapping("/api/users")
|
|
349
|
+
public ResponseEntity<User> create(@Valid @RequestBody CreateUserRequest request) {
|
|
350
|
+
User user = userService.create(request);
|
|
351
|
+
return ResponseEntity.status(HttpStatus.CREATED).body(user);
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Validation failures trigger `MethodArgumentNotValidException`; handle them in an `@ExceptionHandler` to return 400 with error details. Standard annotations include `@NotNull`, `@NotBlank`, `@Size`, `@Min`, `@Max`, `@Email`, `@Pattern`. For validation groups or custom validators, use `@Validated` with a group class or implement `ConstraintValidator`; see the Bean Validation spec for details.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Spring for GraphQL — Boot 4
|
|
2
|
+
|
|
3
|
+
**Spring Boot**: 4.0.x | **Spring for GraphQL**: aligned with Boot BOM | **Jakarta EE**: 11
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Dependencies](#1-dependencies)
|
|
10
|
+
2. [application.yaml](#2-applicationyaml)
|
|
11
|
+
3. [Schema (SDL)](#3-schema-sdl)
|
|
12
|
+
4. [Query and mutation controllers](#4-query-and-mutation-controllers)
|
|
13
|
+
5. [Records for types](#5-records-for-types)
|
|
14
|
+
6. [Security](#6-security)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. Dependencies
|
|
19
|
+
|
|
20
|
+
Spring Boot 4 BOM manages Spring for GraphQL. Add the starter:
|
|
21
|
+
|
|
22
|
+
```kotlin
|
|
23
|
+
// build.gradle.kts
|
|
24
|
+
dependencies {
|
|
25
|
+
implementation("org.springframework.boot:spring-boot-starter-graphql")
|
|
26
|
+
implementation("org.springframework.boot:spring-boot-starter-web") // or webflux
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 2. application.yaml
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
spring:
|
|
36
|
+
graphql:
|
|
37
|
+
graphiql:
|
|
38
|
+
enabled: true # dev: UI at /graphiql
|
|
39
|
+
path: /graphql # default
|
|
40
|
+
cors:
|
|
41
|
+
allowed-origins: "https://myapp.example.com"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The GraphQL endpoint is exposed at `spring.graphql.path` (default `/graphql`). Use the same `SecurityFilterChain` as for REST to protect it; see section 6.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 3. Schema (SDL)
|
|
49
|
+
|
|
50
|
+
Place schema files under `src/main/resources/graphql/**/*.graphqls` (or `.graphqls`). Boot auto-picks them.
|
|
51
|
+
|
|
52
|
+
Example `src/main/resources/graphql/schema.graphqls`:
|
|
53
|
+
|
|
54
|
+
```graphql
|
|
55
|
+
type Query {
|
|
56
|
+
product(id: ID!): Product
|
|
57
|
+
products(first: Int = 10): [Product!]!
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
type Mutation {
|
|
61
|
+
createProduct(input: CreateProductInput!): Product!
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
type Product {
|
|
65
|
+
id: ID!
|
|
66
|
+
name: String!
|
|
67
|
+
sku: String!
|
|
68
|
+
price: Float!
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
input CreateProductInput {
|
|
72
|
+
name: String!
|
|
73
|
+
sku: String!
|
|
74
|
+
price: Float!
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 4. Query and mutation controllers
|
|
81
|
+
|
|
82
|
+
Use `@Controller` and `@QueryMapping` / `@MutationMapping`. Method names match schema field names by default, or specify with the annotation.
|
|
83
|
+
|
|
84
|
+
```java
|
|
85
|
+
import org.springframework.graphql.data.method.annotation.Argument;
|
|
86
|
+
import org.springframework.graphql.data.method.annotation.MutationMapping;
|
|
87
|
+
import org.springframework.graphql.data.method.annotation.QueryMapping;
|
|
88
|
+
import org.springframework.stereotype.Controller;
|
|
89
|
+
|
|
90
|
+
@Controller
|
|
91
|
+
public class ProductGraphQLController {
|
|
92
|
+
|
|
93
|
+
private final ProductService productService;
|
|
94
|
+
|
|
95
|
+
public ProductGraphQLController(ProductService productService) {
|
|
96
|
+
this.productService = productService;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@QueryMapping
|
|
100
|
+
public Product product(@Argument String id) {
|
|
101
|
+
return productService.findById(id);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@QueryMapping
|
|
105
|
+
public java.util.List<Product> products(@Argument Integer first) {
|
|
106
|
+
int limit = first != null ? first : 10;
|
|
107
|
+
return productService.findAll(limit);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@MutationMapping
|
|
111
|
+
public Product createProduct(@Argument CreateProductInput input) {
|
|
112
|
+
return productService.create(input.name(), input.sku(), input.price());
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
For nested types (e.g. `Product` has a `category` field), use `@SchemaMapping` on a method that takes the parent object and returns the nested one:
|
|
118
|
+
|
|
119
|
+
```java
|
|
120
|
+
@SchemaMapping(typeName = "Product", field = "category")
|
|
121
|
+
public Category category(Product product) {
|
|
122
|
+
return categoryService.findById(product.categoryId());
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 5. Records for types
|
|
129
|
+
|
|
130
|
+
Use Java records for DTOs and inputs. Spring for GraphQL maps them to the schema:
|
|
131
|
+
|
|
132
|
+
```java
|
|
133
|
+
public record CreateProductInput(String name, String sku, double price) {}
|
|
134
|
+
|
|
135
|
+
public record Product(String id, String name, String sku, double price, String categoryId) {}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Ensure record component names align with schema field names (or use `@JsonProperty` if names differ). Use `jakarta.*` and JSpecify where applicable for nullability in the API layer.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## 6. Security
|
|
143
|
+
|
|
144
|
+
GraphQL is exposed over HTTP like REST. Protect the GraphQL path in your `SecurityFilterChain` (see `references/spring-security-7.md`): require authentication for `/graphql` and optionally allow anonymous for introspection in dev only. For authenticated GraphQL, clients typically send the same token (e.g. Bearer JWT) in the `Authorization` header; the resolver can access the current user via `SecurityContextHolder` or a custom context in the GraphQL execution.
|
|
145
|
+
|
|
146
|
+
Restrict introspection in production if you do not want to expose the full schema to unauthenticated clients.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
**Summary:** Use `spring-boot-starter-graphql` with Boot 4 BOM, define the schema in `.graphqls` under `resources/graphql`, and implement queries and mutations with `@Controller`, `@QueryMapping`, and `@MutationMapping`. Use records for input and output types. Secure the GraphQL endpoint with the same Spring Security 7 configuration as REST.
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
7. [Module Integration Testing](#7-module-integration-testing)
|
|
15
15
|
8. [Generating Documentation](#8-generating-documentation)
|
|
16
16
|
9. [Common Pitfalls](#9-common-pitfalls)
|
|
17
|
+
10. [DDD & Modulith](#10-ddd--modulith)
|
|
17
18
|
|
|
18
19
|
---
|
|
19
20
|
|
|
@@ -231,3 +232,13 @@ Output lands in `target/spring-modulith-docs/` (Maven) or `build/spring-modulith
|
|
|
231
232
|
| **Forgetting the verification test in CI** | Add the module verification test (section 3) to your CI pipeline so boundary violations are caught on every commit. |
|
|
232
233
|
| **Direct service calls across modules** | One module must not inject another module's service and call it directly. Use `ApplicationEventPublisher` and `@ApplicationModuleListener` (or transactional events) for cross-module communication. |
|
|
233
234
|
| **Putting shared DTOs in one module's internal** | Types used in events or APIs consumed by several modules belong in a `shared/` (or similar) package, not in one module's `internal/`. |
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## 10. DDD & Modulith
|
|
239
|
+
|
|
240
|
+
**Aggregate:** An aggregate is a cluster of entities and value objects with a **root entity** that enforces invariants. It is loaded and persisted as a unit. In Modulith, a module usually has one or more aggregates; the module’s public service (e.g. `OrderService`) orchestrates the aggregate: it loads the root via a repository, mutates it, and saves. Example: `Order` as aggregate root; `OrderService.placeOrder(...)` loads or creates an `Order`, applies domain logic, calls `OrderRepository.save(order)`, and publishes events. The repository interface lives in the module API; the implementation (JdbcClient or JPA) lives in `internal/`.
|
|
241
|
+
|
|
242
|
+
**Domain repository:** A **domain repository** is an interface that exposes only domain operations (e.g. `save(Order)`, `findById(OrderId)`), without infrastructure details. In Spring terms, that interface is the **port**; the implementation in `internal/` (using JdbcClient, JPA, etc.) is the **adapter**. The repository you already use for the module (e.g. `OrderRepository`) acts as the domain repository if it only exposes domain-centric methods and returns domain types or optionals.
|
|
243
|
+
|
|
244
|
+
**Domain events vs application events:** A **domain event** is something that happened in the domain (e.g. “OrderPlaced”). An **application event** is the mechanism: you publish with `ApplicationEventPublisher` and other modules listen with `@ApplicationModuleListener`. In practice with Modulith, the events you publish between modules are application events that typically **represent** domain events of the publishing module. The payload (e.g. `OrderPlacedEvent`) carries the domain data. Section 5 (Event-Driven Inter-Module Communication) and section 6 (Transactional Event Listeners) describe how to publish and consume these; here we only distinguish: domain event = meaning; application event = Spring’s delivery mechanism.
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Spring Data Redis — Boot 4
|
|
2
|
+
|
|
3
|
+
**Spring Boot**: 4.0.x | **Spring Data Redis**: aligned with Boot BOM | **Jakarta EE**: 11
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Dependencies](#1-dependencies)
|
|
10
|
+
2. [application.yaml](#2-applicationyaml)
|
|
11
|
+
3. [RedisTemplate and StringRedisTemplate](#3-redistemplate-and-stringredistemplate)
|
|
12
|
+
4. [Redis as cache backend](#4-redis-as-cache-backend)
|
|
13
|
+
5. [Pub/Sub (optional)](#5-pubsub-optional)
|
|
14
|
+
6. [Session store (optional)](#6-session-store-optional)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. Dependencies
|
|
19
|
+
|
|
20
|
+
Spring Boot 4 BOM manages Spring Data Redis. Add the starter:
|
|
21
|
+
|
|
22
|
+
```kotlin
|
|
23
|
+
// build.gradle.kts
|
|
24
|
+
dependencies {
|
|
25
|
+
implementation("org.springframework.boot:spring-boot-starter-data-redis")
|
|
26
|
+
// Optional: use Redis as distributed cache backend (see section 4)
|
|
27
|
+
// implementation("org.springframework.boot:spring-boot-starter-cache")
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
For connection pooling (Lettuce is the default client):
|
|
32
|
+
|
|
33
|
+
```kotlin
|
|
34
|
+
// Lettuce is included transitively; no extra dependency for basic use
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 2. application.yaml
|
|
40
|
+
|
|
41
|
+
```yaml
|
|
42
|
+
spring:
|
|
43
|
+
data:
|
|
44
|
+
redis:
|
|
45
|
+
host: localhost
|
|
46
|
+
port: 6379
|
|
47
|
+
password: # set in production
|
|
48
|
+
timeout: 2000ms
|
|
49
|
+
lettuce:
|
|
50
|
+
pool:
|
|
51
|
+
max-active: 16
|
|
52
|
+
max-idle: 8
|
|
53
|
+
min-idle: 4
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
For a single Redis URL:
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
spring:
|
|
60
|
+
data:
|
|
61
|
+
redis:
|
|
62
|
+
url: redis://user:password@localhost:6379/0
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 3. RedisTemplate and StringRedisTemplate
|
|
68
|
+
|
|
69
|
+
Use `StringRedisTemplate` for string keys/values; use `RedisTemplate<String, Object>` (with a configured serializer) for objects. Prefer strings when possible for simplicity and interoperability.
|
|
70
|
+
|
|
71
|
+
```java
|
|
72
|
+
import org.springframework.data.redis.core.StringRedisTemplate;
|
|
73
|
+
import org.springframework.stereotype.Service;
|
|
74
|
+
|
|
75
|
+
@Service
|
|
76
|
+
public class TokenStore {
|
|
77
|
+
|
|
78
|
+
private final StringRedisTemplate redis;
|
|
79
|
+
|
|
80
|
+
public TokenStore(StringRedisTemplate redis) {
|
|
81
|
+
this.redis = redis;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public void save(String key, String value, java.time.Duration ttl) {
|
|
85
|
+
var ops = redis.opsForValue();
|
|
86
|
+
ops.set(key, value, ttl);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public String get(String key) {
|
|
90
|
+
return redis.opsForValue().get(key);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Hashes and lists:
|
|
96
|
+
|
|
97
|
+
```java
|
|
98
|
+
redis.opsForHash().put("user:1", "name", "Alice");
|
|
99
|
+
redis.opsForHash().put("user:1", "email", "alice@example.com");
|
|
100
|
+
redis.opsForList().rightPush("queue:tasks", taskId);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Use Records or simple DTOs when storing JSON; serialize with Jackson and store as string, or use `RedisTemplate<String, YourRecord>` with `GenericJackson2JsonRedisSerializer` (ensure Jakarta and Boot 4 Jackson versions align).
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 4. Redis as cache backend
|
|
108
|
+
|
|
109
|
+
For distributed caching, use Redis instead of Caffeine. Enable caching and set the cache type to Redis:
|
|
110
|
+
|
|
111
|
+
```yaml
|
|
112
|
+
spring:
|
|
113
|
+
cache:
|
|
114
|
+
type: redis
|
|
115
|
+
cache-names: products,users
|
|
116
|
+
data:
|
|
117
|
+
redis:
|
|
118
|
+
host: localhost
|
|
119
|
+
port: 6379
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```java
|
|
123
|
+
@SpringBootApplication
|
|
124
|
+
@EnableCaching
|
|
125
|
+
public class Application { ... }
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Use `@Cacheable`, `@CacheEvict`, `@CachePut` as with any Spring Cache backend. TTL can be configured per cache via `spring.cache.redis.time-to-live` or custom `RedisCacheConfiguration` if needed.
|
|
129
|
+
|
|
130
|
+
Dependencies: `spring-boot-starter-cache` and `spring-boot-starter-data-redis`. No extra starter for Redis cache; Boot auto-configures it when `type: redis` is set.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 5. Pub/Sub (optional)
|
|
135
|
+
|
|
136
|
+
Publish messages with `RedisTemplate`:
|
|
137
|
+
|
|
138
|
+
```java
|
|
139
|
+
redis.convertAndSend("notifications", "User signed up: " + userId);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Subscribe with a listener:
|
|
143
|
+
|
|
144
|
+
```java
|
|
145
|
+
import org.springframework.data.redis.connection.Message;
|
|
146
|
+
import org.springframework.data.redis.connection.MessageListener;
|
|
147
|
+
import org.springframework.data.redis.listener.ChannelTopic;
|
|
148
|
+
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
|
149
|
+
import org.springframework.context.annotation.Bean;
|
|
150
|
+
import org.springframework.context.annotation.Configuration;
|
|
151
|
+
|
|
152
|
+
@Configuration
|
|
153
|
+
public class RedisPubSubConfig {
|
|
154
|
+
|
|
155
|
+
@Bean
|
|
156
|
+
RedisMessageListenerContainer redisMessageListenerContainer(
|
|
157
|
+
org.springframework.data.redis.connection.RedisConnectionFactory factory,
|
|
158
|
+
NotificationSubscriber subscriber) {
|
|
159
|
+
var container = new RedisMessageListenerContainer();
|
|
160
|
+
container.setConnectionFactory(factory);
|
|
161
|
+
container.addMessageListener(subscriber, new ChannelTopic("notifications"));
|
|
162
|
+
return container;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Implement `MessageListener` and handle `message.getBody()` in `onMessage`.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 6. Session store (optional)
|
|
172
|
+
|
|
173
|
+
To store HTTP sessions in Redis, add Spring Session:
|
|
174
|
+
|
|
175
|
+
```kotlin
|
|
176
|
+
implementation("org.springframework.session:spring-session-data-redis")
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Configure session timeout and optionally namespace in `application.yaml`. Sessions are then stored in Redis instead of in-memory; suitable for multi-instance deployments. Security and session configuration remain in Spring Security 7; see `references/spring-security-7.md` for auth.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
**Summary:** Use `spring-boot-starter-data-redis` with Boot 4 BOM, configure host/port/pool in `application.yaml`, and use `StringRedisTemplate` or `RedisTemplate` for keys/values, hashes, and lists. Use `spring.cache.type=redis` for distributed cache; optionally use pub/sub or Spring Session Data Redis for sessions.
|