reactive-programmingspring-webfluxspring-reactive

How to consolidate the results of Mono Objects in spring WebFlux?


Please look at the below code from controller(Added comments) which uses RestTemplate:

@GetMapping("/{courseid}")
public Course getCourseDetails(@PathVariable Long courseid) {

    // Get Course info (ID, Name, Description) from pre-populated Array List
    CourseInfo courseInfo = getCourseInfo(courseid);
    
    // Get Price info of a course from another microservice using RESTTemplate
    Price price = restTemplate.getForObject("http://localhost:8002/price/"+courseid, Price.class);
    
    // Get enrollment info of a course from another microservice using RESTTemplate
    Enrollment enrollment = restTemplate.getForObject("http://localhost:8003/enrollment/"+courseid, Enrollment.class);
    
    //Consolidate everything in to Course object and send it as response
    return new Course(courseInfo.getCourseID(), courseInfo.getCourseName(), courseInfo.getCourseDesc(), price.getDiscountedPrice(),
            enrollment.getEnrollmentOpen());
}

Now I am trying to achieve the same using Reactive programming. I now use WebClient and Mono from Web-Flux. But, I am so confused as to how to combine the results? Take a look at the below code (Just using Mono Everywhere. Rest of the code remained same)

@GetMapping("/{courseid}")
public Mono<Course> getCourseDetails(@PathVariable Long courseid) {

    // Get Course info (ID, Name, Description) from pre-populated Array List
    CourseInfo courseInfo = getCourseInfo(courseid);
    
    // Get Price info of a course from another microservice using RESTTemplate
    Mono<Price> price = webClient.get().uri("http://localhost:8002/price/{courseid}/",courseid).retrieve().bodyToMono(Price.class);
    
    // Get enrollment info of a course from another microservice using RESTTemplate
    Mono<Enrollment> inventory = webClient.get().uri("http://localhost:8003/enrollment/{courseid}/",courseid).retrieve().bodyToMono(Enrollment.class);
    
    //Question : How do we Consolidate everything and form a Mono<Course> object and send it as response?
    
}

Question 1 : How do we Consolidate everything and form a Mono object and send it as response?

Question 2 : Does the statement "CourseInfo courseInfo = getCourseInfo(courseid);" cause blocking operation?

Thanks!


Solution

  • Answering to:

    Question 1 : How do we Consolidate everything and form a Mono object and send it as response?

    Mono.zip(..) is what you need to combine the two results. This diagram is from the doc :

    zip doc

    Note that, zip will result in an empty Mono if one of A or 1 is empty! Use switchIfEmpty/defaultIfEmpty to protect against that case.

    Thus the code looks like:

    @GetMapping("/{courseid}")
    public Mono<Course> getCourseDetails(@PathVariable Long courseid) {
    
        CourseInfo courseInfo = getCourseInfo(courseid);
        Mono<Price> priceMono = webClient.get().uri("http://localhost:8002/price/{courseid}/",courseid).retrieve().bodyToMono(Price.class);
        Mono<Enrollment> enrollmentMono = webClient.get().uri("http://localhost:8003/enrollment/{courseid}/",courseid).retrieve().bodyToMono(Enrollment.class);
        
        return Mono.zip(priceMono, enrollmentMono).map(t -> new Course(courseInfo.getCourseID(), courseInfo.getCourseName(), courseInfo.getCourseDesc(), t.getT1().getDiscountedPrice(),
                t.getT2().getEnrollmentOpen()));
        
    }
    
    

    Now answering to:

    Question 2 : Does the statement "CourseInfo courseInfo = getCourseInfo(courseid);" cause blocking operation?

    Since you mentioned that Get Course info (ID, Name, Description) from pre-populated Array List, if it is just an in-memory Array containing the course information, then it's not blocking.

    But (as @mslowiak also mentioned), if getCourseInfo contains logic which involves querying a database, ensure that you are not using a blocking JDBC driver. If so, then there is no point of using Webflux and Reactor. Use Spring R2DBC if that is the case.