We have come pretty far in our Saga Pattern Implementation in our last post. We have completed all the services that form a part of our Order Management Saga. Also, we have managed to start up an instance of the Axon Server and connect all our services to it.

Now is the time to see our Saga implementation in action.

To recap, below is our high-level plan for this series about Saga Pattern Implementation.

In Part 1, we spent our time learning about Saga Pattern and its use. We also set the groundwork for the Order Management Saga that we would implement.

Next, in Part 2, we started implementing the Order Management Saga. We finished with the Order Service and the Core-APIs.

Further on, in Part 3, we continued to implement the other services i.e. the Payment Service and the Shipping Service. We also set up the Axon Server that forms a core part of the Axon Framework.

In Part 4 (this post), we will test our Saga and see it in action.

If you’ve directly reached this post, I would strongly recommend you to go through all the above posts in order.

Understanding the Saga Pattern Implementation

To recap what our Saga is supposed to do, let’s revisit the illustration we looked at in the first post of this series.

orchestration based saga pattern implementation

Basically, the Order Service is where the Saga steps are defined. Even though the Saga spans three services, the Order Management Saga class acts as the orchestrator.

As can be seen in the illustration, the Saga is initiated by creating a new Order using the Order Service.

The communication between the services, however, is handled by means of the Axon Server. On starting the three services along with the Axon Server, we get the below view on the Axon Dashboard.

axon server overview saga pattern implementation

In other words, all three services are registered to the Axon Server. Also, all the commands that we have declared are registered as well.

axon framework commands overview

Initiating the Saga

Now is the time to initiate the Saga. To do so, we can simply visit the Swagger UI exposed by the Order Service.

saga pattern implementation swagger

We are basically placing a new order using the Order API exposed by our service. On pressing the Execute button, a new order id is generated.

saga pattern swagger ui order service

This will also initiate the Order Management Saga.

The Saga Execution

By creating a new Order, we have actually initiated the Saga. Let’s see what happened.

In Axon Server, you can see the Commands that have been issued in the Commands Tab.

axon commands saga pattern

As we can see here, the Create Order Command was issued by the Order Service. The Payment Service issued the Create Invoice Command. The Shipping Service issued the Create Shipping Command. Lastly, the Order Service also issued the Update Order Status Command.

These commands are also responsible for publishing events. We can see the count of the number of events in the Overview tab. See below:

overview saga pattern axon dashboard

If you remember, we have used Event Sourcing to store our Aggregates. In case you don’t know what is Event Sourcing, you can refer to a detailed post on Event Sourcing with Axon Framework and Spring Boot.

With Axon Server, you can look at the Event Store in the Search tab.

axon server event store saga pattern

If you see closely, you will be able to see events on all Aggregates i.e. Order Aggregate, Payment Aggregate and the Shipping Aggregate.

All of these events occurred as part of the Order Management Saga. They were initiated just by creating a new Order.

Conclusion

We have successfully completed Saga Pattern Implementation using Axon Framework and Spring Boot.

Of course, a real Order Management Saga for a typical e-commerce setup will have lot many rules and possible outcomes. But we have kept the example simple to focus on the core concept behind Saga Pattern Implementation using Orchestration.

The code for this Saga Pattern example is available on Github.


Saurabh Dashora

Saurabh is a Software Architect with over 12 years of experience. He has worked on large-scale distributed systems across various domains and organizations. He is also a passionate Technical Writer and loves sharing knowledge in the community.

40 Comments

sudarshan · May 28, 2019 at 6:41 am

Hi… This post is really helpful in understanding Saga.

Can you please send me source code of this .

    Saurabh Dashora · June 3, 2019 at 12:12 pm

    Hi Sudarshan, Thanks for your gr8 feedback. You can access the source code for this post on Github.

Jonathan Meraz · June 6, 2019 at 2:55 pm

Thanks for this article, Saurabh.

How does the order-service module handle outages in the middle of saga execution? Here’s an example scenario:

The order-service is the module with the saga logic. Perhaps it goes down when payment-service is placing an invoiceCreatedEvent on the bus.

When order-service comes back up, how does OrderManagementSaga avoid re-processing old events (like the orderCreatedEvent is has already processed), while ensuring it processes events it has not yet touched, beginning with the invoiceCreatedEvent?

I appreciate your help.

    Saurabh Dashora · June 9, 2019 at 4:46 am

    Hi Jonathan,
    Conditions regarding outages are handled by the Axon Server. The server ensures that each command is processed by an instance of the service in which the command is registered. The Saga state for a particular instance is also recorded by Axon.

    Apart from that, one also needs to ensure that the various services run in high-availability mode with multiple instances ready to process a request in case one instance goes down.

      Jonathan · June 12, 2019 at 2:36 pm

      Hello, Saurabh.

      I tested the code in the example by creating a few orders, then stopping and re-starting the order-service. The result was the events which had already been handled by the saga were replayed through the saga. This was undesirable because payment and shipping commands were again sent. Additional commands should not be sent just because a service goes down and comes back up.

      Is there a way to prevent this behavior?

      Thanks for any help.

        Saurabh Dashora · June 13, 2019 at 11:34 am

        Hi Jonathan…Interesting find. I didn’t try this out. Maybe, there is something available in the Axon documentation

        Timi · September 5, 2019 at 12:49 pm

        Hi Jonathan, did you find the reason for this behaviour and a solution? I am experiencing the same issue and don’t fully understand the framework yet to be able to solve it. I read about similar questions on the axon forum, but got no real answer yet. Thanks!

Jonathan Meraz · June 12, 2019 at 3:38 pm

Saurabh,

Your orchestration pattern example is helpful. Do you have an example of a pub-sub pattern with Axon? I can’t find one anywhere.

    Saurabh Dashora · June 13, 2019 at 11:35 am

    Not yet Jonathan. However, I plan to explore it in the future.

Jonathan Meraz · June 12, 2019 at 7:45 pm

Saurabh,
To be very clear, I’m looking for an example of the choreography saga pattern using Axon.

    Saurabh Dashora · June 13, 2019 at 11:38 am

    I don’t have such an example with me right now. And I have not tried it yet. However, I would imagine that if you use choreography saga pattern using axon you will merely be using Axon as an event bus. In which case, you could probably do great without using Axon itself and use something else like RabbitMQ or Kafka as message broker.

    Just my 2 cents.

      Jonathan Meraz · June 13, 2019 at 1:02 pm

      Much thanks, Saurabh.

Poonam P · July 11, 2019 at 2:45 pm

Saurabh,
How can we showcase a rollback is happening in this service if any event failed?
Reply will help me a lot

    Saurabh Dashora · July 13, 2019 at 3:51 am

    Hi Poonam,

    Rollback in a Saga will be in the form of compensating transactions and NOT a traditional database rollback. In this example, we will have to extend it by showing a case such as if shipping fails for some reason, we reverse the payment and also change the status of the Order.

    Basically, the rollback state will be defined by your business requirements and it will be achieved by issuing compensating transactions.

Poonam P · July 12, 2019 at 8:59 am

Saurabh,
I’ve tried running this application in which the first order-service is building correctly and working fine, but while building payment-service and shippment-service [ERROR] Failed to execute goal org.apache.maven.plugins:maven-clean-plugin:3.1.0:clean (default-clean) on project core-apis: Failed to clean project: Failed to delete C:\Users\pparaska\Desktop\AXON\saga-pattern-axon-spring-boot-sample\core-apis\target\core-apis-0.0.1-SNAPSHOT.jar -> [Help 1]the build is getting failed getting this error
Please help if possible

    Saurabh Dashora · July 13, 2019 at 3:59 am

    Hi Poonam,

    To build your code, you can open from the main POM file at the root level. Then, you can start up each application using the below commands.

    clean package spring-boot:run -pl order-service –also-make
    clean package spring-boot:run -pl payment-service –also-make
    clean package spring-boot:run -pl shipping-service –also-make

    This will ensure that the dependencies i.e. the core-apis are also built along with the individual applications and included in the jar.

Ravi · August 14, 2019 at 12:07 pm

Hi I am getting this error
{
“timestamp”: “2019-08-14T12:06:57.760+0000”,
“status”: 500,
“error”: “Internal Server Error”,
“message”: “AxonServerRemoteCommandHandlingException{message=An exception was thrown by the remote message handling component., errorCode=’AXONIQ-4002′, server=’9848@costbo-HP-Notebook’}”,
“path”: “/api/orders”
}

    Saurabh Dashora · August 15, 2019 at 3:58 am

    Hi Ravi,

    As per official Axon docs, the error code AXONIQ-4002 is related to Command Execution. Check out the link.

    However, in your specific case, could you elaborate on the steps you have performed that led to this error?

      Anonymous · August 16, 2019 at 7:07 am

      Hi I just followed Step Downloaded project from link-https://github.com/dashsaurabh/saga-pattern-axon-spring-boot-sample then downloaded Axon Server Jar and run command for Axon Jar =java -jar axonserver.jar for services
      mvn clean package spring-boot:run -pl order-service –also-make
      mvn clean package spring-boot:run -pl payment-service –also-make
      mvn clean package spring-boot:run -pl shipping-service –also-make
      then connect to order service swagger and did post opreation for creating order in Response got that error

        Saurabh Dashora · August 16, 2019 at 11:50 am

        Hmm…interesting. However, I just tried the whole thing again and it worked as described.

        Is there nothing else in the logs such as the Axon Server run time logs and the Spring Boot application logs?

    Coder · September 9, 2019 at 2:44 pm

    Hi Saurabh, Ravi,

    I have followed the same steps and it was working fine. After that for another run – I am getting below error message.
    Is there any configuration required ?

    {
    “timestamp”: “2019-09-09T14:30:21.500+0000”,
    “status”: 500,
    “error”: “Internal Server Error”,
    “message”: “AxonServerRemoteCommandHandlingException{message=An exception was thrown by the remote message handling component., errorCode=’AXONIQ-4002′, server=’85812@C02VDP9DHV29′}”,
    “path”: “/api/orders”
    }

      Saurabh Dashora · September 10, 2019 at 7:02 am

      Hi, can’t say because I didn’t run into this issue during my trials.

      But you said it worked for the first run and then failed for another run some time later. Did something change between those runs?

        Anonymous · September 10, 2019 at 9:21 am

        No, I din’t change anything. Just tried to stop Shipping service ignorer to check how compensation will happen. Nothing change in code level. May be some issue with AXON server. Ravi also got the same error message. Not sure. Clean up the workspace, checkout the codes again, Restart the AXON server , etc but still not resolved the issue.

        Coder · September 10, 2019 at 9:24 am

        Didn’t change anything in code level. Jut to stop one service in order to check how compensate will happen. Might be some issue with AXON server. Not sure. Clean up the WP, checked out the codes again and restart the AXON server but still getting the same error.

Coder · September 10, 2019 at 9:52 am

It’s worked now. I change the AXON connector jar to 4.1. Thanks.

    Saurabh Dashora · September 11, 2019 at 12:06 pm

    Great. Seems like a version issue then. Glad that it worked now for you!

Coder · September 11, 2019 at 12:50 pm

Thanks. Saurabh, there are few questions –

1. How do you compensate the transactions in case of any service failures ?
2. Do we need to implement any compensate states for rollback / others ?
3. For example – In case there is some failure in Shipping service then the transactions made by Order service and Payment service should be rolled back. How do you handle this type of scenario ?

    Saurabh Dashora · September 14, 2019 at 2:44 am

    Hi,

    1. You have to design compensating transactions. Basically, you can have a timeout that if the Saga doesn’t complete for a particular duration (depending on business case), it should compensate the transactions that have gone through.
    2. Yes, you can implement compensate states. There will be no physical rollback as such.
    3. If it is a business case failure, shipping service can issue a command to reverse the Payment and cancel the order. If it is a technical error (like application going down), you should probably try to retry the messages that got missed. Ideally, if something can cause the shipping to fail for some business reason, it should be accounted for in your commands and events model.

      Coder · September 16, 2019 at 9:14 am

      Thanks Saurabh. I am agreed with you.

Matt Madhavan · September 24, 2019 at 11:56 pm

Hello,
Thanks for the great posts! Can you please point me to some examples where the “rollbacks”/compensatings occur?

Thanks
Matt

Ayush · February 24, 2020 at 7:07 am

Hello,
Can you please help me with the rollback situation in this program. I am not being able to figure out where to add the condition for rollback. I am new to this framework. Please, help me out here.
Thanks

    Saurabh Dashora · February 24, 2020 at 12:42 pm

    Hi Ayush,

    Basically in a Saga, there is no physical rollback of data. What we do is that in the case of a failure, we issue a compensating transaction that reverts the previous transactions in the Saga based on your business requirements. For example, if we have debited the transaction amount from the customer’s bank account in the case of a shopping order and if we later find out that the item is not in stock, we issue a transaction to credit the amount back to the customer.

Dwanil · December 18, 2020 at 10:02 am

Hello,
Thanks for the elaborative post ! How can we replace axon server with kafka ? Can you please help me out.

Thanks
Dwanil

    Saurabh Dashora · December 20, 2020 at 3:14 am

    Hi Dwanil,

    Though I don’t have an example of Kafka. But if you use Kafka, it’s a matter of hooking into the Axon Event creation process and publishing that event to a Kafka topic.

Anon · December 19, 2020 at 11:11 am

Hi,
Thanks for the elaborate and simple to understand posts. It makes the implementation quite clear.
I am trying to use kafka instead of axon server.
In the pom files of individual services. I have removed “axon-server” dependency. And I have also have added to

    Saurabh Dashora · December 20, 2020 at 3:18 am

    Hi, I’m happy that the post helped you and the implementation was understandable.

Dave · December 19, 2020 at 8:48 pm

Hi,
Thanks for the elaborate and simple to understand posts. It makes the implementation quite clear. I am trying to use kafka instead of axon server.
In the pom files of individual services, I have made below changes

1. Removed “axon-server” dependency.
2. Added kakfa-client dependency

However the command Gateway fails(CreateInvoiceCommand) in orderManagmentSaga. It gives the following error.
org.axonframework.commandhandling.NoHandlerForCommandException(No handler was subscribed to command [com.progressivecoder.ecommerce.commands.CreateInvoiceCommand])

Any idea how to go resolve this ? Thanks
Regards,
Dave

    Saurabh Dashora · December 20, 2020 at 3:17 am

    Hi Dave..I’m happy that the post helped you.

    With regards to your issue, I’m curious whether you have completely removed Axon dependency. Because if you do that the Command Gateway will not work.

    Though I do not have a ready made Kafka example for this project, theoretically I would imagine hooking into the event creation process and publishing that event to a Kafka Topic as well.

      Dave · December 20, 2020 at 9:07 pm

      Hi,
      in the pom I am importing Axon as follows.

      org.axonframework
      axon-spring-boot-starter
      4.4.3

      org.axonframework
      axon-server-connector

      Also i have added axon-kafka-spring-boot-autoconfigure and kafka-clients
      In addition, I have given mentioned kafka connection config in application.properties

      If Kafka server is not running, there is a build error when starting any of the micro-services. Also, when listening to the consumer I can see the payload in consumed messages.
      The axon-server is completely removed and the command gateway seems to be partially working. The create order cmd (CreateOrderCommand) seems to work fine.
      But then the CreateInvoiceCommand fails in event handler.
      Any suggestions what can be done?

Leave a Reply

Your email address will not be published. Required fields are marked *