Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support nested sagas #82

Open
cer opened this issue Aug 12, 2022 · 3 comments
Open

Support nested sagas #82

cer opened this issue Aug 12, 2022 · 3 comments

Comments

@cer
Copy link
Collaborator

cer commented Aug 12, 2022

Currently

  • a Saga command handler returns a reply message.
  • The command handler cannot be implemented by a saga.

Proposed:

Support saga command handlers that

  • Return void
  • Have a parameter - SagaReplyInfo - that contains the data needed to construct a reply, e.g. message headers etc.
  • The SagaReplyInfo can be passed to a Saga
  • A Saga's completion callbacks onXXX() - can use the SagaReplyInfo to send a reply to the command
@cer
Copy link
Collaborator Author

cer commented Aug 12, 2022

Builds on eventuate-tram/eventuate-tram-core#179

@cer
Copy link
Collaborator Author

cer commented Aug 16, 2022

Design overview

See NestedSagaTest and other classes in that package.

There are two sagas:

  • OuterSaga - the saga, which has a participant that invokes InnerSaga
  • InnerSaga - the saga that implements a step of OuterSaga

SagaCommandHandler (part of OuterSaga) that initiates InnerSaga

public class ParticipantCommandHandlers {

    public CommandHandlers commandHandlerDefinitions() {
        return SagaCommandHandlersBuilder
                .fromChannel("customerService")
                .onMessage(ReserveCreditCommand.class, this::reserveCredit)

    private void reserveCredit(CommandMessage<ReserveCreditCommand> cm, CommandReplyToken commandReplyToken) {
        InnerSagaData data = new InnerSagaData(commandReplyToken);
        sagaInstanceFactory.create(innerSaga, data);
    }

Key point: the command handler instantiates new InnerSagaData(commandReplyToken), which enables InnerSaga to send a reply to OuterSaga when it completes

Sending a reply when InnerSaga completes

public class InnerSaga implements SimpleSaga<InnerSagaData>  {

    @Override
    public void onSagaCompletedSuccessfully(String sagaId, InnerSagaData innerSagaData) {
        SimpleSaga.super.onSagaCompletedSuccessfully(sagaId, innerSagaData);
        commandReplyProducer.sendReplies(innerSagaData.getCommandReplyToken(), withSuccess());
    }

cer added a commit to eventuate-tram/eventuate-tram-sagas-quarkus that referenced this issue Oct 3, 2022
@warslett
Copy link

We are doing something similar to this with eventuate where we have a high level saga (what we have been calling the parent saga) which initiates child sagas in other services. This works out the box but it has the problem that the parent saga doesn't know when or if the child saga completes successfully. It just starts the child saga and then carries on.

Our solution has been to consume the command from the parent saga using a simple message consumer subscription (instead of an eventuate command handler), where we then start the saga and we set the command message headers to the saga data object. Then we have an observer hooked in to our saga which listens for a success or rollback and at that point sends the relevant reply.

This means that the parent saga doesn't complete until the child saga completes and if the child saga rolls back then it causes the parent saga to rollback. There is still one problem with this solution which is if you have multiple child sagas and a later child saga rolls back after a previous child saga has completed then we will need to create a separate mechanism to roll back the previously completed child saga.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants