Правильный способ выполнения нескольких реактивных операций - PullRequest
0 голосов
/ 16 ноября 2018

Я получаю из реактивного репозитория Mono<FooBar> в зависимости от его значения. Мне нужно создать два других объекта, сохранить их с помощью реактивных репозиториев, изменить объект FooBar и сохранить его.

Поскольку я новичок в реактивном программировании, я нашел следующее решение, которое работает, но я не уверен, правильно ли я использую реактивный API:

@Test
void createAndSave() {
    Mono<FooBar> fooBarMono = findFooBar()  // returns Mono<FooBar>
            .map(fooBar -> {
        createAndSaveLoremBar(fooBar).subscribe();   // returns Mono<LoremBar>
        createAndSaveDoloremBar(fooBar).subscribe(); // returns Mono<DoloremBar>

        fooBar.setActive(true);

        return saveFooBar(fooBar);          // returns Mono<FooBar>
    }).flatMap(Function.identity());

    StepVerifier.create(fooBarMono)
            .expectNextMatches(Objects::nonNull)
            .expectComplete()
            .verify();

}

из журнала консоли:

   saved lorem bar
   saved dolorem bar
   saved foo bar

Ответы [ 2 ]

0 голосов
/ 26 ноября 2018

Я думаю, что приведенное ниже решение немного более читабельно.В любом случае Александр прав, вы никогда не должны изменять входные данные.Вы видите, мы заимствуем много концептуальной формы функционального программирования.Например, вы звоните Function.identity(), это называется identity functorFlux, и Mono - монады.И есть концепция, которая является секретной для этих парней, называется Referential transparency, что обязательный разрыв обновления.

    final Mono<FooBar> fooBarMono1 = findFooBar()
            .zipWhen((fooBar) -> createAndSaveLoremBar(fooBar))
            .map(tuple -> tuple.getT1())
            .zipWhen((fooBar) -> createAndSaveDoloremBar(fooBar))
            .map(tuple -> tuple.getT1())
            .map(fooBar -> new FooBar(true))
            .flatMap(fooBar -> saveFooBar(fooBar));

Или более кратко:

    final Mono<FooBar> fooBarMono1 = findFooBar()
            .zipWhen((fooBar) -> createAndSaveLoremBar(fooBar)
                                    .then(createAndSaveDoloremBar(fooBar)))
            .map(tuple -> tuple.getT1())
            .map(fooBar -> new FooBar(true))
            .flatMap(fooBar -> saveFooBar(fooBar));
0 голосов
/ 17 ноября 2018

Во-первых, мутирование объектов в асинхронном (реактивном) мире не очень хорошая идея.

В любом случае, в вашем решении возможные ошибки при сохранении лорема и долорема игнорируются.Вы можете улучшить его следующим образом:

Mono<FooBar> fooBarMono = findFooBar()
        .flatMap(fooBar -> Flux.merge(
                createAndSaveLoremBar(fooBar),
                createAndSaveDoloremBar(fooBar)) // asynchronously saving lorem and dolorem
                .then(Mono.fromCallable(() -> {  // if there wasn't errors, mutate and save fooBar
                    fooBar.setActive(true);
                    return fooBar;
                }).flatMap(fooBar1 -> saveFooBar(fooBar1))));

Если бы вы могли создать копию вашего fooBar с истинным флагом active, код мог бы быть проще.Например, с помощью lombok.

@Builder(toBuilder = true)
public class FooBar {
...
}

Mono<FooBar> fooBarMono = findFooBar()
        .flatMap(fooBar -> Flux.merge(
                createAndSaveLoremBar(fooBar),
                createAndSaveDoloremBar(fooBar))
                .then(saveFooBar(fooBar.toBuilder().active(true).build())));

И если вас не интересует результат вашего saveFooBar(...), а только сигнал завершения, вы можете сделать все три сохранения асинхронно:

Flux<Object> flux = findFooBar()
        .flatMapMany(fooBar -> Flux.merge(
                createAndSaveLoremBar(fooBar),
                createAndSaveDoloremBar(fooBar),
                saveFooBar(fooBar.toBuilder().active(true).build())));

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

...