The choice we propose in term of web framework is the following.

You can continue to use Spring MVC and all the related well known technologies that we continue to improve: Tomcat, JPA, etc. You can even leverage some reactive bits by using WebClient modern API instead of RestTemplate .

But we also provide a reactive stack that includes WebFlux , a web framework based on Reactive Streams for those who want more scalability, a stack immune to latency (useful for microservices-oriented architecture) and better stream processing capabilities. Other parts of the ecosystem like Spring Data and Spring Security provide reactive support as well.

On controller side, you can see that Spring WebFlux supports natively these reactive types, and you can also see another characteristic of Reactive Streams based API where exceptions are mostly used as an error signal carried by the reactive type instead of being thrown like in regular imperative code.

Flux<User> provides more added value since it allows to process the incoming users as a stream while usage of List<User> as typically used in a blocking stack implies loading all the data in memory before processing it. Notice we could also have used Mono<List<User>> here.

You can see that in such API, void becomes Mono<Void> , User becomes Mono<User> . This allows to use them in a non-blocking way and provide access to a rich set of operators. But it also makes it mandatory to use Mono wrapper and significantly change how you use these API. For example if some operations need to be done sequentially like in the init() method which is straightforward with imperative code, here we have to build a declarative pipeline with the then operator.

If we would have chosen Java, we would have written the following UserRepository class that exposes a reactive API to access SQL databases using the DatabaseClient API provided by Spring Data R2DBC.

Let’s see what that means with concrete code, and let’s use this opportunity to show you how one can use R2DBC (Reactive Streams based alternative to JDBC) and Spring Data R2DBC to access SQL databases in a reactive way.

Until now, using Spring reactive stack using WebFlux required a pretty big shift by switching IO related functionalities (web, persistence) from imperative to declarative/functional style by using APIs like Reactor Mono and Flux or RxJava similar types. This disruptive approach provides real advantages compared to imperative programming, but is also very different and requires a non trivial learning curve.

WebFlux with coroutines API in Kotlin

It is important to understand that Spring reactive support has been built on top of Reactive Streams with interoperability in mind, and that Reactor is used for 2 different purpose:

It is the Reactive Streams implementation that we use everywhere in Spring reactive infrastructure

It is also the default reactive public API exposed

But Spring reactive support has been designed from its inception to adapt easily to other asynchronous or reactive APIs like CompletableFuture , RxJava 2, and now coroutines. In that case we still leverage Reactor internally, adapting at public API level to a different end user reactive API.

It is of course perfectly fine to continue to use Flux and Mono in Kotlin if you prefer this approach, but Spring Framework 5.2 introduces a new major feature: we can now use Kotlin coroutines to leverage Spring reactive stack in a more imperative way.

Coroutines are Kotlin lightweight threads allowing to write non-blocking code in an imperative way. On language side, suspending functions identified by the suspend keyword provide an abstraction for asynchronous operations while on library side kotlinx.coroutines provides functions likes async { } and types like Flow which is Flux equivalent in coroutines world.

Coroutines support is enabled when kotlinx-coroutines-core and kotlinx-coroutines-reactor dependencies are in the classpath:

build.gradle.kts

dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${coroutinesVersion}") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:${coroutinesVersion}") }

So what look like UserRepository and UserController written in Kotlin instead of Java and using coroutines and Flow instead or Mono and Flux ?

class UserRepository(private val client: DatabaseClient) { suspend fun count(): Long = client.execute().sql("SELECT COUNT(*) FROM users") .asType<Long>().fetch().awaitOne() fun findAll(): Flow<User> = client.select().from("users").asType<User>().fetch().flow() suspend fun findOne(id: String): User? = client.execute() .sql("SELECT * FROM users WHERE login = :login") .bind("login", id).asType<User>() .fetch() .awaitOneOrNull() suspend fun deleteAll() = client.execute().sql("DELETE FROM users").await() suspend fun save(user: User) = client.insert().into<User>().table("users").using(user).await() suspend fun init() { client.execute().sql("CREATE TABLE IF NOT EXISTS users (login varchar PRIMARY KEY, firstname varchar, lastname varchar);").await() deleteAll() save(User("smaldini", "Stéphane", "Maldini")) save(User("sdeleuze", "Sébastien", "Deleuze")) save(User("bclozel", "Brian", "Clozel")) } }

You can see here that instead of returning for example Mono<User> , we return User (or more exactly its nullable variant User? ) in a suspending function that can be used in an imperative way. The differences in init() method implementation illustrate that pretty well since we now use regular imperative code instead of chained then invocations.

But wait, how can I use coroutines directly on the DatabaseClient type which is a reactive API based on Mono and Flux ? This is made possible because Spring Data R2DBC also provides Kotlin extensions (see for example this one) which allows you to add coroutines based methods on DatabaseClient once imported. By convention, suspending methods are prefixed by await or suffixed by AndAwait and get a similar name to their Mono based counterparts.

Now let’s have a deeper look to this Flow<User> return type. First, be aware that we are referring to kotlinx.coroutines.flow.Flow , not java.util.concurrent.Flow which is Reactive Streams container type provided with Java 9+.

You will use Flow API like you use Java 8+ Stream or its Kotlin equivalent Sequence , but the huge difference is that it is suitable for asynchronous operations and manages backpressure. So it is Flux equivalent in coroutines world, suitable for hot or cold stream, finite or infinite streams, with the following main differences:

Flow is push-based while Flux is push-pull hybrid

Backpressure is implemented via suspending functions

Flow has only a single suspending collect method and operators are implemented as extensions

Operators are easy to implement thanks to coroutines

Extensions allow to add custom operators to Flow

Collect operations are suspending functions

map operator supports asynchronous operation (no need for flatMap ) since it takes a suspending function parameter

Now let’s have a look to the coroutines version of the controller:

@RestController class UserController(private val userRepository: UserRepository) { @GetMapping("/") fun findAll(): Flow<User> = userRepository.findAll() @GetMapping("/{id}") suspend fun findOne(@PathVariable id: String): User? = userRepository.findOne(id) ?: throw CustomException("This user does not exist") @PostMapping("/") suspend fun save(user: User) = userRepository.save(user) }

Again you can see that the code is very close of regular imperative code that we would have used with Spring MVC.