I'm trying to learn reactive programming by refactoring some currently blocking code. Multiple times I have come across the problem of setting some mutable data object's state from within a Mono-sequence without subscribing to it. In the old code the object's fields' values were calculated by some blocking service which I now also do inside Monos.
So far I've usually been (ab)using flatMap to get the expected behaviour:
initExpensiveObject().flatMap(expObj -> initExpensiveField(expObj).map(expField -> {
expObj.setExpensiveField(expField);
return expObj;
})).subscribe(expObj -> System.out.println("expensiveField: " + expObj.getExpensiveField()));
import reactor.core.publisher.Mono;
public class Main {
/**
* Expensive, lazy object instantiation
*/
public static Mono<ExpensiveObject> initExpensiveObject() {
return Mono.fromCallable(ExpensiveObject::new);
}
/**
* Expensive, async mapping (i.e. database access, network request):
* ExpensiveObject -> int
*/
public static Mono<Integer> initExpensiveField(ExpensiveObject expObj) {
return Mono.just(1);
}
public static class ExpensiveObject {
private int expensiveField = -1;
public int getExpensiveField() {
return expensiveField;
}
public void setExpensiveField(int expensiveField) {
this.expensiveField = expensiveField;
}
}
}
While this flatMap-pattern works, I feel like there should be a more reactive solution. Considering there are so many operators in Mono alone, it intuitively feels wrong to "map" from one object to the same in order to mutate it's state. The "side-effect" operators (doOn*), however, don't allow to easily transform another publisher without subscribing to it.
I'm very much open to design improvements if there is no trivial solution to my problem because the code's design is still to sequential.