I’m working on a project where there are, for the sake of this question, two microservices:
- An new OrderService (Spring Boot)
- 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:
- Switch from a
SimpleCommandBus
->DistributedCommandBus
- Change configuration to enable distributed commands
- Connect the Microservices either using SpringCloud or JCloud
- 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?