Method call after returning Mono<Void>

The Mono will not emit data, so doOnNext will not be triggered. You should use the doOnSuccess instead.

Also, your Mono need to be consumed. Without the code, we don't know if it is or not.

Some example here: I added subscribe() to consume the mono. Depending on the use of your Mono, you will have to do or not the same thing.

This print nothing:

Mono<String> m=Mono.just("test");
Mono<Void> v=m.then();
v.doOnNext(x->System.out.println("OK")).subscribe();

This print "OK":

Mono<String> m=Mono.just("test");
Mono<Void> v=m.then();
v.doOnSuccess(x->System.out.println("OK")).subscribe();

doOnNext, and in general all doOn* reactor methods are side-effect methods. You're not supposed to call them to do I/O work or chain operations, but rather log things and not do anything that would affect the state of the application.

In your code sample, notificationLogReactiveRepository.save returns Mono<Void>. The saveNotificationLog returns void and does not subscribe to the publisher returned by notificationLogReactiveRepository.save. This means the notification will not be saved, because nothing happens until you subscribe.

In this case, it seems you're trying to chain operations - then operators are just made for that. Your code should look like this:

@Override
public Mono<Void> sendEmail(EmailDto emailDto) {
    return mailReactiveClient.sendEmail(message ->
        createMessage(emailDto, emailDto.getBody(), message))
           .then(saveNotificationLog(emailDto));
}

private Mono<Void> saveNotificationLog(EmailDto emailDto) {
    return notificationLogReactiveRepository.save(NotificationLog.builder()
        ...
        .build());
}