Building fully reactive web applications with Spring WebFlux and Spring Data R2DBC
By leveraging the Publish-Subscribe (Pub-Sub) pattern, a cornerstone of reactive programming, WebFlux enables developers to build scalable, non-blocking, and asynchronous web applications with ease.
In a traditional Publish-Subscribe system, the Publisher (event producer) and Subscriber (event consumer) are indeed distinct entities that play separate roles in the event-driven architecture. However, WebFlux takes a more nuanced approach, blurring the lines between the Publisher and Subscriber roles. Indeed, In WebFlux, the Publisher and Subscriber roles are not as distinct as they are in traditional Pub-Sub systems.
The WebFlux architecture combines the publishing and subscribing responsibilities within the Processor module. As a Subscriber, the Processor receives data from an upstream Publisher. This Publisher can be a specific microservice or another WebFlux instance. The data received can be in various formats, such as JSON, XML, or binary data. Upon receiving the data, WebFlux can process it according to its configured logic, which may involve transformations, filtering, aggregation, or other operations. Subsequently, as a Publisher, the Processor publishes the processed data to its Subscribers, which can be other WebFlux instances, microservices, or client applications.
WebFlux is well-suited for scenarios where event processing is an inherent part of the event publication process. This is because WebFlux is designed to handle backpressure, which allows the consumer to control the rate at which events are produced, ensuring that the system can handle the event processing in real time.
To utilize Spring WebFlux in a web application, it is necessary to include the Spring Boot Starter WebFlux dependency in the project’s pom.xml file (if you use the maven build tool). This dependency provides a set of libraries and tools that simplify the development of reactive APIs, including the Reactive Web framework, Project Reactor library, and other essential components.
The @EnableWebFlux is a key annotation in Spring Boot’s reactive web programming model, and it’s essential to understand its role in setting up the foundation for building reactive web applications.
By using @EnableWebFlux, we’re allowing Spring Boot to automatically configure the WebFlux framework. we can then create REST controllers using the @RestController annotation, and define REST endpoints using annotations such as @GetMapping, @PostMapping, @PutMapping, and @DeleteMapping. These endpoints can be used to handle HTTP requests and return responses reactively, taking advantage of the non-blocking and asynchronous capabilities of WebFlux.
By default, Spring Boot applications utilize the Tomcat web server, which is a traditional, blocking, and synchronous web server. However, when we incorporate Spring Boot WebFlux into our project, Spring Boot automatically switches to Netty, a non-blocking and asynchronous web server.
This switch is significant because Netty is designed to handle a high volume of concurrent requests efficiently.
Now, we create a reactive REST API using the Spring WebFlux.
let’s check the implemented method:
Method Signature:
The method is annotated with @PostMapping, indicating it handles HTTP POST requests to the “/address/add” endpoint. It consumes JSON data (MediaType.APPLICATION_JSON_VALUE) and produces a JSON response (MediaType.APPLICATION_JSON_VALUE).
Method Body:
The method takes an Item object as a request body, which is deserialized from the incoming JSON payload. The method returns a Mono object, which is a reactive type in the Project Reactor library.
Reactive Chain:
The method creates a Mono instance with the provided Item object using Mono.just(item). This creates a reactive sequence that emits the Item object.
Mono is a specialization of Publisher that represents a stream of zero or one element. Mono provides a more concise API compared to Flux, making it a suitable choice for scenarios where you’re dealing with a single value or an empty result. On the other hand, Flux is a specialization of Publisher that represents a stream of zero or more elements. It’s commonly used when you expect a list of values.
doOnNext:
The doOnNext operator is used to perform a side effect when the Item object is emitted. In this case, it calls the addItem method of the addressService instance, passing the Item object as an argument. This is where the actual business logic for adding the address is executed.
thenReturn:
The thenReturn operator is used to transform the result of the doOnNext operation into a new ResponseEntity instance. The ResponseEntity is created with a status code of 201 (CREATED) and a response body containing the string “Address is created”.
In Spring Boot 2.x, using ResponseEntity with Mono is a valid approach. However, In Spring Boot 3.x, the recommended approach is to use ServerResponse from the org.springframework.web.reactive.function package instead of ResponseEntity. The ServerResponse class is designed for reactive programming and provides a more functional programming model. So, we will have:
(Please pay attention, WebClient is a non-blocking client that is part of the Spring WebFlux. It is not recommended to use WebClient in a RestController in a Spring Boot project. Instead, it’s recommended to use WebClient in a separate component or layer to call REST services because, it can lead to a tight coupling between the controller and the WebClient, making the code less modular and harder to test. Indeed, By separating the WebClient logic from the controller, you can maintain a clean architecture and ensure that each component has a single responsibility.)
When developing fully reactive applications with Spring WebFlux, it is crucial to adopt a non-blocking I/O model to ensure efficient and scalable performance. The traditional JDBC approach, which is employed by JPA-based applications, including those built with Spring Data JPA, relies on a blocking I/O model that can impede the reactive capabilities of Spring WebFlux.
Spring Data R2DBC, as a part of the Spring Data family, offers a reactive alternative to Spring Data JPA. By leveraging R2DBC, Spring Data R2DBC provides a non-blocking, reactive data access layer that can seamlessly integrate with Spring WebFlux.
In essence, adopting R2DBC and Spring Data R2DBC enables developers to unlock the full potential of Spring WebFlux, ensuring that their applications can handle high volumes of traffic and provide a responsive user experience.
To utilize Spring Data R2DBC in a spring boot project, you need to incorporate the necessary dependency into the build configuration.
To accomplish this, you can add the following dependency to the pom.xml file in Maven:
The @EnableR2dbcRepositories annotation serves as a trigger to enable the R2DBC repositories in a Spring-based application. By adding @EnableR2dbcRepositories, you’re instructing Spring to scan the application’s packages for interfaces that extend R2dbcRepository and to create instances of these repositories.
(Placing the @EnableR2dbcRepositories annotation on the main application class is a more common and recommended practice in Spring Boot projects, as it follows the convention of enabling features during the bootstrap process and promotes a clear separation of concerns.)
We can use R2dbcRepository after adding @EnableR2dbcRepositories annotation.
R2dbcRepository is built on top of R2DBC. it is a reactive and non-blocking API that supports a wide range of databases, including PostgreSQL, MySQL, Microsoft SQL Server, H2, and Oracle. The R2dbcRepository interface provides a set of methods for performing CRUD (Create, Read, Update, Delete) operations on your database. You can then inject this repository into your service class and use its methods to interact with your database. So, we should use it.
(In R2DBC, it’s crucial to utilize the @Column annotation from the org.springframework.data.relational.core.mapping package, rather than the one from javax.persistence. This annotation is specifically designed for R2DBC and serves the purpose of defining the column name and other column-level settings for a given property within a persistence class.
Furthermore, it’s worth noting that when working with R2DBC, the @Entity annotation is not necessary. Instead, using the @Table annotation is sufficient. This is an important distinction to make when developing applications that leverage R2DBC.)
The integration of Spring WebFlux and Spring Data R2DBC provides a powerful foundation for building reactive, non-blocking web applications that interact with relational databases. By leveraging these technologies, developers can create scalable, efficient, and responsive systems that meet the demands of modern web development.
I hope this article is useful for you.