Manage sagas between a microservice that uses axon and one that doesn't?

I’m working on a project where there are, for the sake of this question, two microservices:

  1. An new OrderService (Spring Boot)
  2. A “legacy” Invoice Service (Jersey Web Application)

Additionally, there is a RabbitMQ message broker.

In the OrderService, we’ve used the Axon framework for event-sourcing and CQRS.

We would now like to use sagas to manage the transactions between the OrderService and InvoiceService.

From what I’ve read, in order to make this change, we would need to do the following:

  1. Switch from a SimpleCommandBus -> DistributedCommandBus
  2. Change configuration to enable distributed commands
  3. Connect the Microservices either using SpringCloud or JCloud
  4. Add AxonFramework to the legacy InvoiceService project and handle the saga events received.

It is the fourth point where we have trouble: the invoice service is maintained by a separate team which is unwilling to make changes.

In this case, is it feasible to use the message broker instead of the command gateway. For instance, instead of doing something like this:

public class OrderManagementSaga {

    private boolean paid = false;
    private boolean delivered = false;
    @Inject
    private transient CommandGateway commandGateway;

    @StartSaga
    @SagaEventHandler(associationProperty = "orderId")
    public void handle(OrderCreatedEvent event) {
        // client generated identifiers
        ShippingId shipmentId = createShipmentId();
        InvoiceId invoiceId = createInvoiceId();
        // associate the Saga with these values, before sending the commands
        SagaLifecycle.associateWith("shipmentId", shipmentId);
        SagaLifecycle.associateWith("invoiceId", invoiceId);
        // send the commands
        commandGateway.send(new PrepareShippingCommand(...));
        commandGateway.send(new CreateInvoiceCommand(...));
    }

    @SagaEventHandler(associationProperty = "shipmentId")
    public void handle(ShippingArrivedEvent event) {
        delivered = true;
        if (paid) { SagaLifecycle.end(); }
    }

    @SagaEventHandler(associationProperty = "invoiceId")
    public void handle(InvoicePaidEvent event) {
        paid = true;
        if (delivered) { SagaLifecycle.end(); }
    }

    // ...
}

We would do something like this:

public class OrderManagementSaga {

    private boolean paid = false;
    private boolean delivered = false;
    @Inject
    private transient RabbitTemplate rabbitTemplate;

    @StartSaga
    @SagaEventHandler(associationProperty = "orderId")
    public void handle(OrderCreatedEvent event) {
        // client generated identifiers
        ShippingId shipmentId = createShipmentId();
        InvoiceId invoiceId = createInvoiceId();
        // associate the Saga with these values, before sending the commands
        SagaLifecycle.associateWith("shipmentId", shipmentId);
        SagaLifecycle.associateWith("invoiceId", invoiceId);
        // send the commands
        rabbitTemplate.convertAndSend(new PrepareShippingCommand(...));
        rabbitTemplate.convertAndSend(new CreateInvoiceCommand(...));
    }

    @SagaEventHandler(associationProperty = "shipmentId")
    public void handle(ShippingArrivedEvent event) {
        delivered = true;
        if (paid) { SagaLifecycle.end(); }
    }

    @SagaEventHandler(associationProperty = "invoiceId")
    public void handle(InvoicePaidEvent event) {
        paid = true;
        if (delivered) { SagaLifecycle.end(); }
    }

    // ...
}

In this case, When we receive a message from the InvoiceService in the exchange, we would publish the corresponding event on the command gateway so that the saga would continue.

Questions:

  • Is this a sensible approach or a hack?
  • Is there a documented way of handling this kind of scenario in Axon? If so, can you please provide a link to the documentation or any sample code?

Hello?

Hello Waqqas,

Pretty confident I can help you out here Waqqas.
Firstly though, note that this is an open forum, which is responded upon by community members and AxonIQ staff.
Especially the latter group will do this on a best effort basis. As such, questions might fall between the cracks of all the other work lying around.

Having said that, let’s go back to your scenario, where I would like to add another solution.
Axon Server is a unified message routing solution, for the sake of allowing distributed applications to talk with another with commands, events and queries.
As it wasn’t part of your list, I felt compelled to point out that that could be an option too.
Granted, if the other team was willing to talk a similar language as your team that is.

Think it is time to move over to your question right now:

Is this a sensible approach or a hack?

Whether you would do this in a Saga or a regular Event Handler, both would work in this case.
So, is this a hack? No, I don’t think so. This is just an example of context mapping at play.

The requirement you are hitting is the necessity to translate the Order service’s API over to the Invoice service’s API.
The most important part when doing this is to make sure none of their domain specifics are leaking over into your set up, as that would tie both services together.

Is there a documented way of handling this kind of scenario in Axon? If so, can you please provide a link to the documentation or any sample code?

As this is not Axon specific at all, there is no documentation on our end for the matter (yet).
You have found a necessity to map one context to another and to delegate that process.
A typical candidate of being in charge of such a process is an event handling class, so a Saga could indeed be the solution.
The most commonly used term for AxonIQ’s end when it comes to context mapping is to talk about an Anti Corruption Layer.

I find this Medium blog to be pretty decent on the matter. Maybe it clarifies things for you too.

Trusting all this will help you out Waqqas!

Cheers,
Steven

PS. It is important to note that this mailing list will be discontinued as specified in this thread.
The thread also specifies where to look further for help when it comes to Axon as soon as this mailing list is closed.