Spring Reactive in Microservices using rest calls



I work on a micro-service project using spring boot web flux and here are some services:

  • baseInfoService
  • notificationService
  • accountService
  • orderService
  • performService

I’m implementing a service in OrderService which would has this flow:

@PostMapping("/create")
//@PostMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Mono<OrderDto> createOrder(@RequestParam Publisher<OrderRequestDto> orderRequest){
    return service.createOrder(Mono.from(orderRequest));
}

here is my service:

public Mono<OrderDto> createOrder(Mono<OrderRequestDto> orderRequest){
    rule.validate(orderRequest); //returns Mono<OrderRequestDto>

    //====== in the same time after validation =======//

    //@todo call baseInfoService to get some data

    //@todo call baseInfoService to get some other data

    //@todo call performService and send orderRequest to get some data

    //====== after getting above request do ======//

    //@todo calculate and fill some order data acording above request results        

    //====== from here temporary I will use blocking style ======//

    OrderEntity order = transformer.transform(orderRequest);

    order = repository.save(order); 

    OrderDto orderDto = transformer.transformToDto(order);

    //======== after above calculation and save do these operation in the same time =======// 

    //@todo call accountService and send orderDto to serive
    //failure on this service is important and I should take proper action (there should be a call back instead of continue and sending notif)

    //@todo call notificationService and send orderDto to service

    return Mono.just(orderDto);
}

for now I should use webClient for services calls and in future I would use kafka and spring cloud stream and send requests as events.

  • first question is should I use Publisher<OrderRequestDto> or OrderRequestDto in controller?
  • second question: I have seen many simple samples existing in github and other sites but all just get a mono from repository and pass it to controller to return to user and I couldn’t find a complicated real world example like this scenario. could you please provide the way I should implement it.

thank you.

Answer

First, if you use reactor you shouldnt call blocking apis, as you did it in the save method. When you use webflux, you have a small number of threads and if you block those threads your application performance will be very poor. I suggest using reactive database driver instead.

1, You shouldnt use plane object in the controller, because you have to block the thread to get the object itself. In reactor, you mustnt call blocking operation. Also suggest to use blockhound if you are not sure about what is blocking. It will throw an exception during tests if blocking methods are called.

2, In reactive stream, you have to use the reactive operations, like map, flatmap, etc.. to do operations on your objects.

For example, suppose you want to go through a list of objects, load some data from web to each and save those into the database (note that here I’m going to use mock db and webservice, but you can change those into real services and the essence of the example is the processor. Also I use Kotlin here, which is similar to Java)

//Mock repository
object ReactorDatabase {
    fun saveData(car: Car): Mono<Car> = Mono.just(Car(1, car.producer,car.type))
    fun loadData(): Flux<Vehicle> = Flux.just(Vehicle("Toyota"), Vehicle("Ford"))
}  

//Mock webservice
object ReactiveWebService {
    fun loadFromWeb(): Mono<String> = Mono.just("corolla").delayElement(Duration.ofMillis(100))
}

The processor function:

//Load vehicles
//Map vehicles to cars
//Load car type somewhere from the web and zip with the car
//Add type to car
//Save cars into database
//Return with the saved entites
fun process(): Flux<Car>{
        return ReactorDatabase.loadData()
            .map { Mapper.vehicleToCar(it) }           
            .zipWith(ReactiveWebService.loadFromWeb())
            .map { (car, type) -> Mapper.carWithDetails(car, type) }
            .concatMap { ReactorDatabase.saveData(it) }
    }

As you can see, all methods were called from reactive streams. When you use reactive IO operation, it always returns with a Publisher. You can use flatMap, concatMap… to get the object and push towards on the stream without blocking. Also if your operation does not block then you can call methods like Mappers from map.

With reactor you are not able to use imperative programming style (except using coroutines or similar struffs).

Also here is an example to reactive spring cloud stream.



Source: stackoverflow