2

I have the following Code that creates a product and then logs the creation-success in some kind of history service (the journalClient).

If there is an error in the journal client I want to rollback the product creation.

@Service
public class ProductService {

    public Mono<ProductRS> createProduct(final ProductRQ rq) {
       return productclient.createProduct(rq).flatMap(produktfamilieRS -> {
         return journalClient.createJournalEntry("Dummy-Entry").thenReturn(productRS)
             .doOnError(throwable -> {
                 log.error("Error creating journal Entry - Starting rollback of transaction.");
                 rollbackCreateProduct(productRS.getId());
          });
    });

    private void rollbackCreateProduktfamilie(long id) {
       productClient.deleteProduct(id).subscribe();
    }
@Service
public class JournalClientMockImpl implements JournalClientMock {

  public Mono<Void> createJournalEntry(String body, boolean error) {
    throw new RuntimeExcpeption("Test Error");
  }

My problem is, that the RuntimeException that I throw in the JournalClient does not trigger the Mono.doOnError() in the ProductService.createProduct.

Sticking Point: I found out that returning an Mono.error triggers the outer doOnError:

@Service
public class JournalClientMockImpl implements JournalClientMock {

  public Mono<Void> createJournalEntry(String body, boolean error) {
      return Mono.error(new RuntimeException("Beabsichtiger Fehler im JournalServiceMock"));
  }

But my problem with that is, that there could be errors in my business logic that cause RuntimeExceptions that will not end in a Mono.error.

My bad idea: The only idea I have is to do something like this....surrounding the whole code with a catch-all:

  public Mono<Void> createJournalEntry(String body, boolean error) {
    try{
      // do some business logic
      // ...
      // call journal web service
      // ...
      return Mono.empty();
    } catch (Throwable e){
      return Mono.error(new RuntimeException("Error in JournalServiceMock"));
    }
   
  }

Should this be the solution? This smells like very bad design. How to do this properly?

Thank you very much

MarkusJackson
  • 225
  • 2
  • 12
  • Do on error is for side effects only, like logging etc. If you wish to switch to another publisher you should use for instance onErrorResume. – Toerktumlare Jul 14 '21 at 16:39

1 Answers1

1

Try .onErrorResume instead of .doOnError

@Service
public class ProductService {

    public Mono<ProductRS> createProduct(final ProductRQ rq) {
       return productclient.createProduct(rq).flatMap(produktfamilieRS -> {
         return journalClient.createJournalEntry("Dummy-Entry").thenReturn(productRS)
             .onErrorResume(throwable -> {
                 log.error("Error creating journal Entry - Starting rollback of transaction.");
                 rollbackCreateProduct(productRS.getId());
          });
    });

    private void rollbackCreateProduktfamilie(long id) {
       productClient.deleteProduct(id).subscribe();
    }
M Sezgin R
  • 71
  • 4