Объедините два моно вместе, где второй моно подписан на первый

Недавно я изучал реактивное программирование с использованием библиотек реакторов в Java со средой Spring, и по большей части мне удалось с этим справиться. Однако пару раз я оказывался в такой же ситуации и хотел бы посоветовать, где я ошибаюсь.

Суть того, с чем я борюсь, часто заключается в том, что я хочу что-то сделать с моно, например, найти некоторые дополнительные данные, а затем добавить их обратно в исходный моно. Функция zip кажется мне идеальным кандидатом, но затем я дважды подписываюсь на исходный моно, что не входит в мои намерения.

Вот надуманный пример ситуации, которую я пытался решить, поскольку не могу поделиться кодом своей компании. Предполагается, что мы используем библиотеку реактивных данных и настроили регистратор, а класс Person неизменен, но имеет методы ‹FieldName›.

public Mono<Person> getPersonWithFamilyMembers(Integer id){
    log.info("Finding person with id {}", id);

    personRepository.findById(id)
        .switchIfEmpty(Mono.error(NotFoundException::new))
        .doOnNext(person -> log.info("Found person: {}", person))
        .as(this::fetchAndAddFamilyMembers)
        .doOnSuccess(person -> log.info("Successfully found person with family members"));
}

private Mono<Person> fetchAndAddFamilyMembers(Mono<Person> personMono){
    Mono<List<Person>> familyMembersMono = personMono
        .map(Person::getFamilyId)
        .flatMapMany(PersonRepository::findByFamilyId)
        .collectList();

    return personMono.zipWith(familyMembersMono, Person::withFamilyMembers);
}

Результат, который я вижу при запуске такого кода:

INFO | Finding person with id 1
INFO | Found person: Person(id=1, familyId=1, familyMembers=[])
INFO | Found person: Person(id=1, familyId=1, familyMembers=[])
INFO | Successfully found person with family members

Это имеет смысл, поскольку исходный моно человека был подписан в двух местах, где я сопоставляю его с familyMembersMono, и когда я объединяю их вместе, но я не хочу делать ненужные вызовы в репозиторий, если я могу этого избежать.

Есть ли у кого-нибудь предложение, как лучше справиться с таким поведением?


person YoungCarrdinal    schedule 11.02.2020    source источник


Ответы (1)


В общем, вы не «добавляете данные» в Mono, а только данные внутри. Имея это в виду, используйте flatMap вместо as:

public Mono<Person> getPersonWithFamilyMembers(Integer id){
    log.info("Finding person with id {}", id);

    return personRepository.findById(id)
        .switchIfEmpty(Mono.error(NotFoundException::new))
        .doOnNext(person -> log.info("Found person: {}", person))
        .flatMap(this::fetchAndAddFamilyMembers)
        .doOnSuccess(person -> log.info("Successfully found person with family members"));
}

private Mono<Person> fetchAndAddFamilyMembers(Person person){ // this accepts Person, not Mono<Person>
    return personRepository.findByFamilyId(person.getFamilyId())
        .collectList()
        .map(person::withFamilyMembers);
}
person Panda    schedule 12.02.2020