Hi all, as the title Reactive Java: Combining Mono(s) suggests, here we are going to use spring boot web flux to combine multiple Mono(s) into a single operation using the zipWith() method.
We focus a lot on non-blocking and asynchronous communication in a reactive micro-service architecture. Spring boot has provided the support for the same. Spring’s support for Reactive micro-service is based on the MONO and FLUX API’s at its core. What exactly does it mean to be reactive? The answer is the ability to react to a change almost immediately, like rendering of UI components on mouse click/hover actions.
The example of mouse clicks/hovering on UI is quite straight forward. One very important thing to consider with non-blocking reactive applications is back pressure. We have to provide ways to handle back pressure. In a non-blocking environment, it becomes important to control the rate of events so that a fast producer does not overwhelm its destination. This is where reactive streams comes into picture. Reactive streams defines the interaction between the asynchronous components with back pressure. The main purpose of reactive streams is to let the subscriber control how fast or slow a publisher produces data. Spring web flux uses project reactor at its core for providing the support of reactive streams. It provides the mono and flux API types to work on data sequences 0..1 and 1..n respectively.
In this post we will focus on:
1. How we combine multiple mono’s together?
2. What could go wrong if we do not consider the basics?
Scenario
Let us go through the following code snippets:
In the snippet’s above we have defined a simple `BlogController.java` class. This gets you the blog details and its ratings. User has to provide a blog id in the request to get the details and blog rating. For the sake of simplicity we will use a static Map<Integer, String> as our backend data source. The code is quite simple. We inject a repository instance to our controller class, and then invoke the repository methods. We combine the result obtained from the 2 repository calls using the Mono.zipWith() function. Ideally, if our static map contains blog Id we should be able to see the blog detail and its rating. Now, let’s run the service and try to hit the endpoint.

We can see that the response includes the blog name and rating for that blog. We were able to combine two mono’s into one and manipulate their response. So far so good, but Wait!! Aren’t we missing something? What happens if the blog id we send in the URL does not exist in our map? Will the second mono fetch the blog rating, if the first mono results empty? Will second repository call execute? Let’s see…
Empty Mono

Basics!! Yes, we forgot the basic of using the zipWith() method of Mono API. As per the documentation for the zipWith() method:
It Combines the result from this mono and another into a Tuple2.
An error or empty completion of any source will cause the other source
to be cancelled and the resulting Mono to immediately error or complete, respectively.
We forgot to capture the state when our repository call would return an empty Mono. That is why we see that when first repository call returned Mono.empty the second repository call was never made/processed. The program execution was simply dropped thereafter, and empty response was sent back immediately to the user.
Solution:
The easiest way to solve such a problem is to provide a switchIfEmpty() clause to your Mono calls. When our backend/3rd-party call results in a Mono.empty() we should not by pass the program execution. We should continue to execute the rest of the code statements. We should not halt the application processing. Therefore, we should be careful when combining the mono execution and should provide a fallback mechanism. Whenever the Mono results in an empty state the switchIfEmpty() method acts as a fallback. We can update the BlogController.java class to use the switchIfEmpty(). Let’s see how that would help, see the updated controller below:
Now that we have added the switchIfEmpty() to the mono call this would provide a fallback when any mono results in empty. So there won’t be a scenario that when the first repo call reslut in Mono.empty(), second call won’t get executed. This way we can make sure that both the repository calls will execute. Let’s confirm that by hitting the same endpoint for an invalid id:

It is clear from the above snapshot, both the repository calls execute and our fallback works as expected. That is how we improve the usage of zipWith() for mono chaining.
I hope you liked the blog, in a way that it would encourage you to code on your own and find other hidden aspects of Mono chaining in Spring. If you have any query regarding the topic please feel free to ask that in the comments section below. Any suggestions or improvements are more than welcome. As a result, i would be able to come up with a better one in the future. Happy coding. 🙂
References:
- https://docs.spring.io/spring-framework/docs/5.0.0.BUILD-SNAPSHOT/spring-framework-reference/html/web-reactive.html
- https://spring.io/
