Skip to content

Execution

Domo42 edited this page Aug 16, 2014 · 3 revisions

There can be multiple sagas handling one and the same message. Consider the following use case: There is a super class named OrderRequest with several inheritors like CancelOrderRequest, SubmitOrderRequest, ...

With the saga-lib one can write a saga that is triggered by either the concrete class or a base class or an interface. This allows it to have seperate logic triggered based on the same message. If there are multiple sagas all matching the same input, every one after the other will be called.

Lets write two saga handlers, one for the base class one for the concrete implementation.

The first one will check whether the account id (defined on the base class) actually exists in the system. Please note that the handler method parameter is of the supers class type. If the account itself does not exist it will stop the execution of any further saga handlers, even though they might match given message.

public class AccountCheckerSaga : AbstractSingleEventSaga {
  private final AccountService accountService;

  @Inject
  public AccountCheckerSaga(final AccountService accountService) {
    this.accountService = accountService;
  }

  @StartsSaga
  public void checkAccountExists(final OrderRequest orderRequest) {
    if (!accountService.doesAccountExist(orderRequest.getAccountId())) {
      context().stopDispatchingCurrentMessageToHandlers();
    }
  }
}

The second saga handles the concreate CancelOrderRequest and performs the cancel operation.

public class CancelOrderSaga : AbstractSingleEventSaga {
  private final OrderService orderService;

  @Inject
  public CancelOrderSaga(final OrderService orderService) {
    this.orderService = orderService;
  }

  @StartsSaga
  public void cancelOrder(final CancelOrderRequest orderRequest) {
    orderService.cancelOrder(orderRequest.getOrderId());
  }
}

Splitting the account check and the cancel operation has the advantage that the account check is reused for any kind or order operation. In addition the logic inside the CancelOrderSaga is more focused on the actual cancel operation and does not need to contain repetitive checking logic. It will only be called if the AccountCheckerSaga lets the message "through" by not calling stopDispatchingCurrentMessageToHandlers().

There is one thing to consider though. This only works if the checker saga is executed before all the others. The saga-lib out-of-the-box comes with the rule that @StartsSaga handlers will be executed before any @EventHandler sagas. Otherwise it does not perform the execution of the sagas in any particular order. If a more detailed specification is needed in can be defined during the initialization by calling defineHandlerExecutionOrder().

MessageStream msgStream = EventStreamBuilder.configure()
        .usingSagaProviderFactory(sagaProvider)
        .defineHandlerExecutionOrder()
            .firstExecute(LogHandlerSaga.class)
            .then(AccountCheckerSaga.class)
        .builder().build();

The above example specifies two sagas to be executed before all others. First the LogHandlerSaga will be called and then the AccountCheckerSaga. There can be any number of additional instructions regarding execution order by chaining additional then calls. Sagas not specified will be called after the others without any particular ordering.

Clone this wiki locally