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.