Search code examples
spring-webfluxspring-cloud-gateway

Join the result of several microservice in a Mono


I try to develop a api with spring-cloud-gateway but it's not easy for me

My "microservice A" return an Mono. This Object contains a List of id "Object B" My "microservice B" return an Flux

In my api-gateway, how can i aggregate in a Mono the result of the microservices ?

@Service
public class ServiceAClient {

    private final WebClient webClient;

    public ServiceAClient(WebClient.Builder builder) {
        this.webClient = builder.baseUrl("lb://microservice-A/A/").build();
    }

    public Mono<ObjectA> getObjectA(String id){

        return webClient
                .get()
                .uri("{id}" , id)
                .retrieve()
                .bodyToMono(ObjectA.class)
                .onErrorResume(ex->Mono.empty());
    }
}
@Service
public class ServiceBClient {

    private final WebClient webClient;

    public ServiceAClient(WebClient.Builder builder) {
        this.webClient = builder.baseUrl("lb://microservice-B/B/").build();
    }

    public Flux<ObjectB> getListObjectB(List<Long> ids){

        return webClient
                .get()
                .uri("{ids}" , ids)
                .retrieve()
                .bodyToFlux(ObjectB.class);
    }
}
@Data
public class ObjectA {
    private UUID id;
    private String name;
    private String description;
    private Date start;
    private Date end;
    private List<Long> listIdObjectB;
}

@Data
public class ObjectB {
    private Long id;
    private String name;
    private String localisation;
    
}
@Data
public class MyDto {
    private UUID id;
    private String name;
    private String description;
    private Date start;
    private Date end;
    private List<ObjectB> listObjectB;
}

@Service
@AllArgsConstructor
public class CombinedService {

    private final ServiceAClient serviceAClient;
    private final ServiceBClient serviceBClient;

    public Mono<MyDto> getDetails(String id){
        // return MyDto who join a Mono Service A an Flux service B


    }
    
  
}

The desired result

{
    "id": "2355e7eb-edf7-428c-b51b-ac06c146ed3c",
    "name": "toto",
    "description": "lorem ipsum",
    "debut": 01/06/2022,
    "fin": 10/06/2022,
    "ListObjectB": [
        {
            "id": 1,
            "name": "foo",
            "localisation": "here"
        },
        {
            "id": 2,
            "name": "bar",
            "localisation": "here"
        }
    ]
}

Thank you in advance for your help


Solution

  • You can take it as a start point to understand a possible solution. I highly recommend you to read about reactive streams(https://www.reactive-streams.org/) and spring-webflux(https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html).

    Your CombinedService should looks like:

    @Service
    @AllArgsConstructor
    public class CombinedService {
    
        private final ServiceAClient serviceAClient;
        private final ServiceBClient serviceBClient;
    
        public Mono<MyDto> getDetails(String id) {
            return serviceAClient.getObjectA(id)
                .map(objectA -> {
                    final Flux<ObjectB> fluxB = serviceBClient.getListObjectB(objectA.getListIdObjectB());
                    final List<ObjectB> listObjectB = fluxB.toStream().collect(Collectors.toList());
    
                    final MyDto myDto = new MyDto();
    
                    myDto.setName(objectA.getDescription());
                    myDto.setDescription(objectA.getDescription());
                    myDto.setListObjectB(listObjectB);
    
                    //More setters, etc
    
                    return myDto;
            });
        }
    }