spring

Using zipWhen() With Mono

With Mono zipWhen function, Java developers can elegantly manage dependencies between asynchronous operations, ensuring seamless integration within reactive applications. Let us delve into exploring the Spring Reactor’s Mono.zipWhen() functionality and use cases.

1. Introduction

Reactive programming has become increasingly popular in the development of non-blocking, asynchronous applications. Spring Reactor is a key project in the Spring ecosystem for building reactive applications. It provides several reactive types, including Mono and Flux, to handle data streams and implement reactive patterns. In this context, the Mono.zipWhen() method is a powerful tool for orchestrating different asynchronous operations.

The Mono.zipWhen() method is part of the Spring Reactor library, designed for composing and transforming data reactively. It allows developers to execute a secondary asynchronous operation once the first operation represented by a Mono has been completed successfully, and then combine the results of both operations into a single Mono. It is particularly useful when you have a sequence of dependent operations where the outcome of the first operation is required to execute the second operation.

1.1 How does Mono.zipWhen() work?

The basic usage of Mono.zipWhen() involves providing a function that takes the result of the initial Mono and returns another Mono representing the second operation. Once both operations have been completed, their results are combined using a provided combinator function.

Mono<Type1> firstMono = ...;

Mono<Type2> secondMono = firstMono.zipWhen(result1 -> {
	return ...; // create second Mono using result1
}, (result1, result2) -> new CombinedResult(result1, result2));

In this example, firstMono is the initial Mono, and the lambda expression inside zipWhen() creates the second Mono based on the result of firstMono. Finally, the combinator function combines the results of both Monos into a new instance of CombinedResult.

1.2 Use Cases

The Mono.zipWhen() method is ideal for scenarios where two or more reactive streams need to be executed sequentially, and their results must be combined. Common use cases include:

  • Combining results from two database queries where the second query depends on the result of the first.
  • Performing a network call based on the result of a previous computation or call.
  • Sequentially executing two or more independent but related asynchronous operations and combining their results.

1.3 Advantages

Utilizing zipWhen() in scenarios like our user registration process provides several advantages:

  • Sequential Execution: Ensures that the welcome email is only sent after the user has been successfully saved to the database.
  • Result Combination: Allows for the combination of results from the initial and dependent operations, even though in this case we chose to ignore the email-sending result.
  • Reactive Composition: Facilitates the composition of multiple asynchronous operations in a reactive pipeline, promoting cleaner and more maintainable code.

2. zipWhen() Example

Imagine we are developing a microservice that needs to fetch a user’s basic details and then, based on those details, fetch additional information like the user’s order history from another service. This is a typical scenario where Mono.zipWhen() shines, allowing us to orchestrate these dependent calls reactively.

Here’s how you how implement this scenario using Mono.zipWhen():

// Service Layer.

package com.jcg.example;

import reactor.core.publisher.Mono;
import java.util.List;

public class UserService {

    // Define a method that encapsulates the provided code
    public void fetchUserProfile() {
        // Fetch user details
        Mono<User> userMono = Mono.just(new User("1", "John Doe"));

        // Use zipWhen to fetch user order history after user details are fetched
        Mono<UserProfile> userProfileMono = userMono.zipWhen(user -> {
            // Simulate fetching user order history
            return Mono.just(new OrderHistory(List.of("Order 1", "Order 2")));
        }, (user, orderHistory) -> new UserProfile(user.getId(), user.getName(), orderHistory.getOrders()));

        // Subscribing to the Mono to trigger the operations and handle the result
        userProfileMono.subscribe(userProfile -> {
            System.out.println("User Profile: " + userProfile);
        });
    }
}

In this code:

  • userMono simulates fetching user details.
  • zipWhen is used to fetch the user’s order history after the user details are successfully retrieved.
  • The result is a UserProfile object that combines both pieces of information.

2.1 Key Takeaways

The Mono.zipWhen() method is extremely useful for handling sequential reactive operations that depend on each other’s outcomes. This example demonstrates how to use it to fetch and combine data from two different sources reactively.

2.2 Dependencies

The code demonstrates the usage of Mono.zipWhen() in a Spring WebFlux application. To run the example code ensure you have a class annotated with the @SpringBootApplication annotation and will need to include the following dependencies in your project.

Assuming you are using Maven for dependency management, here’s what you should add to your pom.xml file:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-core</artifactId>
</dependency>

Ensure you have the Spring Boot parent in your pom.xml to manage versions automatically.

2.3 Output

Upon successful execution, the console will print:

User Profile: UserProfile{id='1', name='John Doe', orders=[Order 1, Order 2]}

This output indicates that the initial user details were successfully fetched and used to simulate fetching the user’s order history. The zipWhen method then combines these details into a UserProfile object, which is printed to the console. The reactive nature of this operation ensures that it is executed in a non-blocking manner, making efficient use of resources and allowing other operations to proceed concurrently.

3. Conclusion

In conclusion, the Mono.zipWhen() operator in the Spring WebFlux framework offers a powerful mechanism for orchestrating complex, dependent asynchronous operations in a reactive programming model. By allowing developers to execute a secondary operation only after the successful completion of a primary operation and then combine the results of both operations, Mono.zipWhen() facilitates seamless data flow and integration in reactive applications. This capability is especially valuable in modern, highly interactive web applications that demand efficient, non-blocking I/O operations to handle concurrent users and processes.

The practical example provided illustrates not just the syntax but also the conceptual application of Mono.zipWhen(), demonstrating how to fetch and combine data from multiple sources reactively. As reactive programming continues to gain traction in the development community, understanding and effectively utilizing operators like Mono.zipWhen() will be critical for building resilient, scalable, and responsive applications. Embracing the reactive programming model and its patterns enables developers to tackle the challenges of modern application development, ensuring applications are more efficient and user-friendly.

Yatin

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button