Reactive Flow with MongoDB, Kotlin, and Spring WebFlux

1. Overview

In this tutorial, we’ll write a simple application showcasing a fully reactive flow using Spring Data Reactive MongoDB and Spring SSeEmitter.

On one side, we’ll apply Spring Data Reactive MongoDB to save data through a Mongo reactive database and combine it with the Server-Sent-Events mechanism to notify subscribed clients about incoming data.

Additionally, we’ll take advantage of Spring Boot’s Kotlin support.

So, let’s start!

2. Setup

First of all, we have to configure our Maven project adding the Spring Data Reactive MongoDB dependency in our pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>

Moreover, to use Kotlin, we’ll need to add the Kotlin standard library to the same file:

<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-stdlib</artifactId>
</dependency>

Now, we’re ready to start developing our application. We’ll start configuring the environment to support reactive programming and Mongo DB, so let’s go!

3. Reactive Mongo Configuration

The first thing we have to do is to configure our project to support reactive Spring Data. We’ll add a new class extending from AbstractReactiveMongoConfiguration to configure the Mongo reactive client and the Spring Data Repository:

@Configuration
@EnableReactiveMongoRepositories(
  basePackageClasses = arrayOf(EventRepository::class))
class MongoConfig : AbstractReactiveMongoConfiguration() {
 
    override fun getDatabaseName() = "mongoDatabase"
 
    override fun reactiveMongoClient() = mongoClient()
 
    @Bean
    fun mongoClient() = MongoClients.create()
 
    @Bean
    override fun reactiveMongoTemplate()
     = ReactiveMongoTemplate(mongoClient(), databaseName)
}

This configuration isn’t required if we want to interact with MongoDB in a non-reactive manner. Note that we have to add the @EnableReactiveMongoRepositories tag to let the configuration know where our Spring Data repositories are. 

Following this, we’re now ready to start implementing the main functionality. The first thing we’ll do is develop a new data class to persist the incoming information and then, a related Spring Data reactive repository to manage that persistence.

4. Document

The document is the unit of storing data in a MongoDB database. This unit uses JSON style for storing data.

In our project, we’ll keep it simple using a dummy document called Event with two attributes: id and name:

@Document
class Event(id: String, name: String)

5. Spring Data Reactive Repository 

The goal of Spring Data abstraction is to reduce the amount of code required to implement data access layers for persistence stores.

Consequently, the reactive version works the same so, we’ll have the following line to implement a whole reactive repository:

interface EventRepository : ReactiveMongoRepository<Event, String>

6. Controller

The Controller class will be responsible for sending a Server-Sent Event whenever any reactive data is saved.

The method saveAndSend will first save the incoming data into our Mongo Reactive database delegating this action to our EventRepository. 

Consequently, we’ll add a new endpoint that creates and saves new Events.

First, let’s see the Kotlin code:

@GetMapping(value = "/save", 
  produces = arrayOf(MediaType.TEXT_EVENT_STREAM_VALUE))
fun saveAndSend(@RequestParam("eventName") eventName: String) =
  eventRepository
    .save(Event(UUID.randomUUID().toString(), eventName))
    .flux()

As we can see, after saving the new data, the Spring Data reactive repository will return an SSE that will be sent to the subscribed client.

At this point, we can say that we have a complete reactive Kotlin server-side project. We already have all the needed elements to execute our Spring Boot application.

Thus, we’ll now take a look at how to create a simple web client to send and receive all our created Server-Sent events.

7. Subscriber

Here we have a simple web client that will be able to save data and receive changes from the server.

Let’s see how it’s implemented:

7.1. Send Data

The client will save the typed event name through the Save new event button.

This, in turn, will make an HTTP request to our server endpoint saveEvent:

<form method="get" action="/save">
    <input type="text" name="eventName">
    <button type="submit">Save new event</button>
</form>

7.2. Receive Data

On the other hand, the client will be listening to save endpoint as well. Note that each programming language has specific frameworks to manage SSE.

However, for our example, we’ll keep it as simple as possible:

<div id="content"></div>
<script>
    var source = new EventSource("save");
source.addEventListener('message', function (e) {
        console.log('New message is received');
        const index = JSON.parse(e.data);
        const content = `New event added: ${index.name}<br>`;
        document.getElementById("content").innerHTML += content;
    }, false);
</script>

8. Conclusion

In conclusion, Spring Data MongoDB has been updated to leverage the reactive programming model introduced in Spring Framework 5. We now have a simple way to use this programming paradigm and Server-Sent events.

Hence, this supposes an alternative to the traditional non-reactive applications the problems with the database blocking.

The implementation of this example can be checked in the GitHub project.

This is a Maven-based project, so then, execute the Spring Boot application to see how it works. Don’t forget to run the Mongo DB server first.Comments are closed on this article!

Related posts:

Java Program to Find kth Smallest Element by the Method of Partitioning the Array
Interface trong Java 8 – Default method và Static method
Java Program to Check if a Given Graph Contain Hamiltonian Cycle or Not
How to Define a Spring Boot Filter?
Mệnh đề if-else trong java
Java Program to Implement Stein GCD Algorithm
Vấn đề Nhà sản xuất (Producer) – Người tiêu dùng (Consumer) và đồng bộ hóa các luồng trong Java
Ignore Null Fields with Jackson
Java Program to Check whether Directed Graph is Connected using BFS
Spring Boot - OAuth2 with JWT
Using JWT with Spring Security OAuth
Generate Spring Boot REST Client with Swagger
How to Use if/else Logic in Java 8 Streams
Jackson Date
Deque và ArrayDeque trong Java
Guide to PriorityBlockingQueue in Java
Java Program to Check if a Directed Graph is a Tree or Not Using DFS
Java NIO2 Path API
New Features in Java 11
Spring Boot - Flyway Database
Spring REST API + OAuth2 + Angular (using the Spring Security OAuth legacy stack)
Giới thiệu Google Guice – Dependency injection (DI) framework
Java Program to Search Number Using Divide and Conquer with the Aid of Fibonacci Numbers
Java Program to Check whether Directed Graph is Connected using DFS
Spring Boot: Customize the Jackson ObjectMapper
Java Program to Implement Range Tree
Hướng dẫn Java Design Pattern – Interpreter
Java Program to Find Second Smallest of n Elements with Given Complexity Constraint
How to Read HTTP Headers in Spring REST Controllers
Java Program to Find the Shortest Path from Source Vertex to All Other Vertices in Linear Time
Giới thiệu Java Service Provider Interface (SPI) – Tạo các ứng dụng Java dễ mở rộng
A Custom Data Binder in Spring MVC