What is Reactive Programming?
Reactive Programming is a programming paradigm focused on asynchronous data streams and non-blocking operations. The Blackjack API uses Spring WebFlux with Project Reactor to achieve high scalability and efficient resource usage.Why Reactive?
Non-Blocking I/O
Threads aren’t blocked waiting for database or network operations, enabling high concurrency.
Backpressure
Consumers can signal producers to slow down, preventing overwhelming of downstream components.
Resource Efficiency
Handle thousands of concurrent requests with a small thread pool.
Composability
Chain and transform asynchronous operations declaratively.
Reactive Stack
| Technology | Purpose |
|---|---|
| Spring WebFlux | Reactive web framework |
| Project Reactor | Reactive streams implementation (Mono, Flux) |
| Spring Data R2DBC | Reactive relational database access (MySQL) |
| Spring Data MongoDB Reactive | Reactive MongoDB access |
Mono and Flux
Project Reactor provides two main types:Mono<T>
Represents a stream of 0 or 1 element.- Single database record lookup
- HTTP response with one result
- Creating/updating a single entity
Flux<T>
Represents a stream of 0 to N elements.- Querying multiple records
- Streaming large datasets
- Server-Sent Events (SSE)
Reactive Repositories
Repositories in the Blackjack API return reactive types.GameRepositoryPort (Interface)
Location:domain/port/GameRepositoryPort.java:7-11
PlayerRepositoryPort (Interface)
Location:domain/port/PlayerRepositoryPort.java:10-15
findRanking() returns Flux<Player> because it retrieves multiple players ordered by score.Reactive MongoDB Adapter
Location:infrastructure/persistence/mongo/MongoGameRepositoryAdapter.java:18-28
Spring Data MongoDB Reactive
Location:infrastructure/persistence/mongo/SpringDataMongoGameRepository.java:5-6
ReactiveMongoRepository automatically provides reactive methods like:Mono<T> save(T entity)Mono<T> findById(ID id)Flux<T> findAll()Mono<Void> deleteById(ID id)
Reactive MySQL Adapter
Location:infrastructure/persistence/mysql/MySqlPlayerRepositoryAdapter.java:20-36
Reactive Operators Explained
defaultIfEmpty()
defaultIfEmpty()
If the stream is empty (player not found), emit a default value (new PlayerRow).
flatMap()
flatMap()
Transform each emitted item into a new Mono/Flux and flatten the result.
map()
map()
Synchronously transform each emitted item.
switchIfEmpty()
switchIfEmpty()
Switch to an alternative stream if the original is empty.
Spring Data R2DBC
Location:infrastructure/persistence/mysql/SpringDataR2dbcPlayerRepository.java:8-18
R2DBC (Reactive Relational Database Connectivity) provides reactive, non-blocking access to relational databases like MySQL and PostgreSQL.
Reactive Use Cases
CreateNewGameUseCase
Location:application/usecase/CreateNewGameUseCase.java:22-40
Reactive Flow Breakdown
PlayMoveUseCase
Location:application/usecase/PlayMoveUseCase.java:24-59
Advanced Reactive Patterns
then() and thenReturn()
then() and thenReturn()
then(): Ignore the emitted value and complete.thenReturn(value): Ignore emitted value and return a specific value.Mono.empty()
Mono.empty()
Returns a
Mono that completes without emitting any value.Mono.error()
Mono.error()
Returns a
Mono that immediately signals an error.Reactive Controllers
GameController
Location:infrastructure/web/controller/GameController.java:43-47
Spring WebFlux automatically:
- Subscribes to the
Mono<CreateGameResult> - Waits asynchronously for the result (non-blocking)
- Serializes the result to JSON
- Sends HTTP 201 response
Handling Multiple Results
Location:infrastructure/web/controller/RankingController.java
Error Handling
Reactive Error Propagation
Errors in reactive streams propagate downstream automatically.Global Exception Handler
Location:infrastructure/web/error/GlobalExceptionHandler.java
Exceptions thrown in reactive streams are caught by
@ExceptionHandler methods, which return Mono<ErrorResponse> for consistent error responses.Reactive Composition Patterns
Sequential Operations
UseflatMap() to chain dependent operations:
Parallel Operations
UseMono.zip() to run operations in parallel:
Conditional Operations
Subscription and Execution
Spring WebFlux automatically subscribes when you return
Mono<T> or Flux<T> from a controller. You don’t need to call .subscribe() manually.Testing Reactive Code
Use StepVerifier fromreactor-test:
Performance Benefits
Traditional Blocking Approach
Reactive Non-Blocking Approach
Thread Pool Size
Blocking: 200+ threads
Reactive: 10-20 threads
Reactive: 10-20 threads
Memory Usage
Blocking: ~200 MB (thread stacks)
Reactive: ~20 MB
Reactive: ~20 MB
Concurrent Requests
Blocking: Limited by threads
Reactive: Thousands easily
Reactive: Thousands easily
Latency
Blocking: Higher under load
Reactive: Consistent
Reactive: Consistent
Reactive Operators Summary
| Operator | Purpose | Example |
|---|---|---|
| map() | Synchronous transformation | .map(game -> toDTO(game)) |
| flatMap() | Async transformation (returns Mono/Flux) | .flatMap(id -> repo.findById(id)) |
| filter() | Conditionally emit items | .filter(game -> game.status() == IN_PROGRESS) |
| switchIfEmpty() | Fallback if empty | .switchIfEmpty(Mono.error(new NotFoundException())) |
| defaultIfEmpty() | Default value if empty | .defaultIfEmpty(newPlayer) |
| then() | Ignore value, signal completion | .then(Mono.just("done")) |
| thenReturn() | Ignore value, return new value | .thenReturn(savedGame) |
| zip() | Combine multiple Monos | Mono.zip(mono1, mono2) |
| Mono.error() | Signal error | Mono.error(new Exception()) |
| Mono.empty() | Complete without value | Mono.empty() |
Best Practices
Use flatMap for Async Operations
Use flatMap for Async Operations
When the transformation returns
Mono<T> or Flux<T>, use flatMap(), not map().Handle Empty Streams
Handle Empty Streams
Always consider what happens if a stream is empty.
Don't Block in Reactive Code
Don't Block in Reactive Code
Never call
.block() in reactive pipelines!Test with StepVerifier
Test with StepVerifier
Use StepVerifier to test reactive streams thoroughly.
Summary
The Blackjack API achieves high scalability through:- Reactive repositories returning
Mono<T>andFlux<T> - Non-blocking database drivers (R2DBC for MySQL, Reactive MongoDB)
- Reactive use cases composing operations with
flatMap(),map(), etc. - Reactive controllers returning reactive types to Spring WebFlux
- Efficient resource usage with small thread pools handling high concurrency
Next Steps
Architecture Overview
See how reactive programming fits into the overall architecture
API Reference
Explore reactive API endpoints