WebFlux Reactive Patterns
Use when the complete Spring Boot 4 request path is reactive: WebFlux controllers, Reactor pipelines, R2DBC persistence, streaming, backpressure, or cancellation. For defining and registering declarative outbound API interfaces, use http-interface-clients.
View SKILL.mdUse WebFlux when the complete request path benefits from non-blocking I/O.
Dependencies and pipeline rules
- Use the dedicated Boot 4 WebFlux and WebFlux test starters.
- Return
MonoorFlux; never callsubscribe()in request-handling code. - Choose
flatMap,concatMap, or sequential composition according to ordering needs. - Keep JDBC, filesystem, and blocking SDK calls off Reactor event loops.
- Isolate unavoidable blocking work on
boundedElasticat the adapter boundary. - Apply timeouts to external calls and preserve cancellation.
Context and errors
- Put request-scoped metadata in Reactor
Context, notThreadLocal. - Configure observability context propagation deliberately.
- Translate domain failures centrally without swallowing cancellation.
- Use
onErrorResumeonly for a defined fallback and avoidonErrorContinue.
Persistence and streaming
- Use R2DBC for reactive database access.
- Bound fan-out concurrency according to downstream capacity.
- Define backpressure, buffering, and maximum in-memory sizes.
- Never collect an unbounded stream solely to simplify downstream code.
Testing
- Use
StepVerifierfor publisher behavior andWebTestClientfor contracts. - Test cancellation, timeout, empty results, errors, ordering, and backpressure.
- Detect blocking calls in tests when practical.
Examples
- See
examples/good-reactive-service.javaandexamples/bad-reactive-service.java.
Official sources
- Spring WebFlux: https://docs.spring.io/spring-framework/reference/web/webflux.html
- Reactor reference: https://projectreactor.io/docs/core/release/reference/
- Spring Data R2DBC: https://docs.spring.io/spring-data/relational/reference/r2dbc.html
Gotchas
- Agent adds MVC and WebFlux starters accidentally - choose the intended web stack.
- Agent calls
block()orsubscribe()in application flow - keep subscription with the runtime. - Agent uses
ThreadLocalfor tenant or trace data - use ReactorContext. - Agent uses unbounded
flatMap- set concurrency from downstream limits. - Agent wraps JDBC with
Mono.just- this still blocks the event loop.
Examples & templates
bad-reactive-service.javaexamples
Source on GitHub
Mono<Order> loadOrder(UUID id) {
Order order = repository.findById(id).block();
return Mono.just(order);
}good-reactive-service.javaexamples
Source on GitHub
import java.time.Duration;
import java.util.UUID;
import reactor.core.publisher.Mono;
final class OrderQueryService {
private final ReactiveOrderRepository repository;
OrderQueryService(ReactiveOrderRepository repository) {
this.repository = repository;
}
Mono<Order> loadOrder(UUID id) {
return repository.findById(id)
.switchIfEmpty(Mono.error(new OrderNotFound(id)))
.timeout(Duration.ofSeconds(2));
}
}
interface ReactiveOrderRepository {
Mono<Order> findById(UUID id);
}
record Order(UUID id) { }
final class OrderNotFound extends RuntimeException {
OrderNotFound(UUID id) {
super("Order not found: " + id);
}
}Verification scope
- Compiled sources
- No
- Runtime checks
- No
Coverage describes checked-in fixtures, not a guarantee for every snippet or project.
Coverage matrix & agent evidenceADD TO YOUR AGENTInstall Boot 4 skills
Install Boot 4 skills
claude plugin marketplace add rrezartprebreza/spring-boot-skills
claude plugin install spring-boot-4-skills@spring-boot-skillscodex plugin marketplace add rrezartprebreza/spring-boot-skills
codex plugin add spring-boot-4-skills@spring-boot-skills