Search code examples
javaspringspring-webfluxspring-webclient

How to return a Reactive Flux that contains a Reactive Mono and Flux?


I'm new to reactive programming and ran into this problem:

[
    {
        "customerDTO": {
            "scanAvailable": true
        },
        "bankAccountDTOs": {
            "scanAvailable": true,
            "prefetch": -1
        }
    }
]

DTO:

public class ResponseClientDTO {
    private Mono<CustomerDTO> customerDTO;
    private Flux<BankAccountDTO> bankAccountDTOs;
}

Service:

public Flux<ResponseClientDTO> getCustomerWithBankAccounts(String customerId){
    Flux<BankAccountDTO> bankAccounts = webClient
        .get()
        .uri(uriBuilder -> 
                uriBuilder.path("customers")
                .queryParam("customerId", customerId).build())
        .accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .bodyToFlux(BankAccountDTO.class);
        
    
    Mono<CustomerDTO> cMono = findOne(customerId);
    
    ResponseClientDTO responseClientDTO = new ResponseClientDTO();
    responseClientDTO.setBankAccountDTOs(bankAccounts);
    responseClientDTO.setCustomerDTO(cMono);
    
    return Flux.just(responseClientDTO);
}

I query an endpoint from another API, and it returns a Flux<BankAccounts> . I want to get the client with all his bank accounts.


Solution

  • That is not what you want in a reactive stack. First, change your DTO (Data Transfer Object) so that it does't include Mono and Flux, but CustomerDTO and List<BankAccountDTO> instead:

    public class ResponseClientDTO {
        private CustomerDTO customerDTO;
        private List<BankAccountDTO> bankAccountDTOs;
    }
    

    Then, you need to rearrange your method to return a Mono<ResponseClientDTO> instead and to change the logic to deal with Flux and Mono:

    public Mono<ResponseClientDTO> getCustomerWithBankAccounts(String customerId){
        Flux<BankAccountDTO> bankAccounts = webClient
            .get()
            .uri(uriBuilder -> 
                    uriBuilder.path("customers")
                    .queryParam("customerId", customerId).build())
            .accept(MediaType.APPLICATION_JSON)
            .retrieve()
            .bodyToFlux(BankAccountDTO.class);
            
        Mono<CustomerDTO> cMono = findOne(customerId);
        
        return bankAccounts.collectList().zipWith(cMono).map(data -> {
            ResponseClientDTO responseClientDTO = new ResponseClientDTO();
            responseClientDTO.setBankAccountDTOs(data.getT1());
            responseClientDTO.setCustomerDTO(data.getT2());
        })
    }
    

    (Sorry for any Java typo but at this point I am too used to Kotlin).

    Consider taking a look at the following useful online resources: