Skip to content

Repository files navigation

webflux-example

Small Spring Boot (WebFlux) + Kotlin coroutines example, backed by R2DBC/H2 in memory DB. Shows how Kotlin coroutine services, official Spring Data coroutine repositories, and legacy Reactor (Mono) code coexist and interop in same app.

Stack

  • Kotlin 2.4.10, JVM toolchain 25
  • Spring Boot 4.1.0 (WebFlux, spring-boot-starter-data-r2dbc, H2 console)
  • kotlinx-coroutines-reactor
  • H2 in-memory DB via R2DBC (schema/data loaded from schema.sql / data.sql on startup)
  • JUnit 5 + kotlin-test + coroutines-test for tests

Project layout

src/main/kotlin/com/example/webfluxexample/
├── WebfluxExampleApplication.kt   entry point
├── domain/Entities.kt             User, Order, Profile, Notification, UserDashboard, OrderSummary, EnrichedOrder
├── repository/Repositories.kt     UserRepository / OrderRepository (CoroutineCrudRepository), LegacyUserRepository (ReactiveCrudRepository)
├── service/
│   ├── DashboardServices.kt       ProfileService, OrderQueryService, NotificationService, DashboardService (parallel fan-out with async/coroutineScope)
│   ├── EnrichmentService.kt       simulated shipping/logistics call (delay instead of Thread.sleep)
│   ├── LegacyUserService.kt       pretend old unconverted Reactor code (returns Mono<User>)
│   └── OrderSummaryService.kt     sequential fetch-then-enrich example
└── web/UserController.kt          REST endpoints

What it demonstrates

  • Coroutine repositories need no wrappingUserRepository / OrderRepository extend CoroutineCrudRepository directly.
  • Bridging legacy Reactor codeLegacyUserRepository (ReactiveCrudRepository, returns Mono<User>) called from a coroutine controller endpoint via .awaitSingle(), no rewrite needed.
  • Exposing coroutine code as MonogetUserReactive wraps a suspend call with mono { } for callers not yet migrated.
  • Fan-out / parallel callsDashboardService runs three independent suspend calls concurrently with async + coroutineScope, each result kept in a named variable (no positional Tuple3).
  • Sequential compositionOrderSummaryService fetches user, then order, then enriches it: plain suspend calls, no chaining operators.

Endpoints

Method Path Description
GET /users/{id} Fetch user via legacy Reactor repository (bridged with awaitSingle())
GET /users/{id}/reactive Fetch user via coroutine repository, exposed as Mono<User>
GET /users/{id}/order-summary User + first order + enrichment (delivery estimate)
GET /users/{id}/dashboard Profile + recent orders + notifications, fetched concurrently

Seeded data (src/main/resources/data.sql): users user-1 (Ada Lovelace), user-2 (Alan Turing); orders order-1, order-2 (user-1), order-3 (user-2).

Running

./gradlew bootRun

App starts on default port 8080. Example:

curl localhost:8080/users/user-1/dashboard

H2 console available (spring-boot-h2console) at /h2-console; DB URL r2dbc:h2:mem:///testdb;DB_CLOSE_DELAY=-1, user sa, no password.

Tests

./gradlew test

Covers DashboardService, OrderSummaryService, and UserController (src/test/kotlin/...).

Build

./gradlew build

About

Spring Weblfux with Kotlin

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages