This is the Part 3 of our Saga Pattern Implementation with Axon and Spring Boot series.

We started to implement Saga Pattern in the previous post.

To recap, below is the high-level plan we have been going through. In case you have directly come to this post, I strongly recommend going through the previous posts in this series.

In Part 1, we went through the basics of Saga. We also discussed about the usage of Axon Framework and Spring Boot for building our application. If you are not sure about what is Saga Pattern, I strongly recommend you to go through that post and then come back to this post.

Next, in Part 2, we implemented two major parts of our application. We basically implemented the Order-Service and the Core-APIs.

In Part 3 (this part), we will continue with our implementation. We will implement other services that form a part of our Saga Pattern Implementation.

Lastly, in Part 4, we will be testing our application.

So let’s continue further with the next steps.

Payment Service Implementation

Payment Service is another typical Spring Boot application. In the context of our application, this service takes care of creating an invoice after the Order is created.

Below are the POM.xml dependencies for this service. It is exactly similar to the Order Service.

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<!-- Axon -->
		<dependency>
			<groupId>org.axonframework</groupId>
			<artifactId>axon-spring-boot-starter</artifactId>
			<version>4.0.3</version>
		</dependency>

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>

		<!-- Swagger -->
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>2.9.2</version>
		</dependency>

		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>2.9.2</version>
		</dependency>

		<dependency>
			<groupId>com.progressivecoder.saga-pattern</groupId>
			<artifactId>core-apis</artifactId>
			<version>${project.version}</version>
		</dependency>

		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
</dependencies>

The Payment Aggregate

The Payment Aggregate is the core for this service. It stores the invoice related information and the relationship with the Order Aggregate.

@Aggregate
public class InvoiceAggregate {

    @AggregateIdentifier
    private String paymentId;

    private String orderId;

    private InvoiceStatus invoiceStatus;

    public InvoiceAggregate() {
    }

    @CommandHandler
    public InvoiceAggregate(CreateInvoiceCommand createInvoiceCommand){
        AggregateLifecycle.apply(new InvoiceCreatedEvent(createInvoiceCommand.paymentId, createInvoiceCommand.orderId));
    }

    @EventSourcingHandler
    protected void on(InvoiceCreatedEvent invoiceCreatedEvent){
        this.paymentId = invoiceCreatedEvent.paymentId;
        this.orderId = invoiceCreatedEvent.orderId;
        this.invoiceStatus = InvoiceStatus.PAID;
    }
}

As you can see it’s a pretty simple entity class. While in a real production case, this will have lots of other details. However, for the purposes of our sample app, I have kept it intentionally simple.

Similar to the Order Service, we are using Event Sourcing to store the Aggregate information. If you want to know more about Event Sourcing, I have a detailed post on Event Sourcing using Axon and Spring Boot.

To elaborate further, the Payment Aggregate has a handler for Create Invoice Command. When this command is received, a new instance of the payment is created and a Invoice Created Event is published.

Application Properties File

We will run the Payment Service on a different port. For doing so, we will set the server.port property in the application.properties file.

Also, note that we are providing an application.name property. This is a good practice as this will help us easily identify our application when it connects to the Axon Server.

spring.application.name=payment-service
server.port=8081

Shipping Service Implementation

The responsibility of the Shipping Service is to create a shipment. This service waits for the appropriate command from the Order Management Saga. On receiving the command, it creates a shipment.

In our application, we will again be using Spring Boot.

Below is the POM.xml for the dependencies used in our application.

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<!-- Axon -->
		<dependency>
			<groupId>org.axonframework</groupId>
			<artifactId>axon-spring-boot-starter</artifactId>
			<version>4.0.3</version>
		</dependency>

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>

		<!-- Swagger -->
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>2.9.2</version>
		</dependency>

		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>2.9.2</version>
		</dependency>

		<dependency>
			<groupId>com.progressivecoder.saga-pattern</groupId>
			<artifactId>core-apis</artifactId>
			<version>${project.version}</version>
		</dependency>

		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
</dependencies>

The Shipping Aggregate

The Shipping Aggregate is the core of the Shipping Service. This Aggregate stores the relationship between the shipment and the order.

Below is the how the Aggregate class definition looks like:

@Aggregate
public class ShippingAggregate {

    @AggregateIdentifier
    private String shippingId;

    private String orderId;

    private String paymentId;

    public ShippingAggregate() {
    }

    @CommandHandler
    public ShippingAggregate(CreateShippingCommand createShippingCommand){
        AggregateLifecycle.apply(new OrderShippedEvent(createShippingCommand.shippingId, createShippingCommand.orderId, createShippingCommand.paymentId));
    }

    @EventSourcingHandler
    protected void on(OrderShippedEvent orderShippedEvent){
        this.shippingId = orderShippedEvent.shippingId;
        this.orderId = orderShippedEvent.orderId;
    }
}

As can be seen, the Aggregate has a handler for the Create Shipping Command. This command is issued by the Order Management Saga.

Once the command is received, the Aggregate publishes the Order Shipped Event. It also sets the values on the Aggregate instance.

Application Properties File

We will update the application.properties file to set the server.port property. Also, we will set the application.name to shipping-service.

spring.application.name=shipping-service
server.port=8082

Setting up Axon Server

Next step for us is to start up the Axon Server. On a high-level, Axon Server is primarily used to facilitate communication between the various services in our Saga Pattern Implementation. If you want to know more, I have a detailed post on Axon Server.

To start up Axon Server, we can download the Axon Server JAR file in zip format from AxonIQ site.

axon server download

As you can see there are several options available. We can run Axon Server as a Docker Image or download the entire platform. However, we will use the Axon Server as ZIP file approach.

Download the ZIP file and extract it somewhere on your system. Then, you can simply get into the directory and run the below command in your command prompt or terminal to run Axon Server.

java -jar axonserver.jar

The Axon Server should start up on port 8024

axon server startup

Next, you can visit http://localhost:8024. If everything has worked fine, you should be presented with the below screen.

axon server dashboard

Starting up the other services

Now that Axon Server is up and running, we need to also start the other services that are part of our Saga Pattern Implementation. These are the Order-Service, the Payment-Service and the Shipment-Service.

To start the three services, we can use the below commands at the root directory of our multi-maven module project structure.

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

Basically, here we are just starting up the individual services. The –also-make parameter ensures that the dependencies for the project are also bundled up in the JAR file that gets created.

Once the three applications have started up successfully, we can see them in the Overview page of the Axon Server Dashboard.

axon server overview saga pattern implementation

Basically, what this view signifies is that all the services are now registered with the Axon Server’s service discovery mechanism.

On the Commands view tab, we can also see all the commands defined in our Saga. On this page, we can also keep track of the number of commands issued in our Saga and so on.

axon framework commands overview

Conclusion

With this, our Saga Pattern Implementation is ready to be tested. However, since this post has become pretty long now, we will cover the testing in the next post.

At this point, we have basically finished the Saga Pattern Implementation for our Order Management Saga. The code for this is available on Github for your reference.

In the next post, we will continue with the testing of our Order Management Saga.

Stay tuned for that. And do sound of your comments in the comments section below.


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.

10 Comments

Nelson · June 6, 2019 at 3:21 am

Congratulations, the post is very well done, another hand, if you could do me a favor with de answer the question,
1. It is possible to dispense with the axon server using sagas for the communication of microservices ?
2. What is the best option to communicate between microservices, I think using RestTemplate, kafka, what do you think ?

    Saurabh Dashora · June 6, 2019 at 11:32 am

    Hi Nelson,

    Thanks for the feedback. It is always great to know that the post is helping people.
    Please find below my views on your questions:

    1. Yes, you can certainly build sagas and communicate between microservices without Axon Server.
    2. I think it depends on the type of requirement you have.If you are looking for a request-response approach, RestTemplate is the way to go. If you are ok to have asynchronous approach, then you can use something like RabbitMQ or Kafka as the messaging solution.

Mouli · May 21, 2020 at 9:21 pm

Hi Saurabh,
This sample implementation is really good.
How does the order-service, payment-service and shipping-service get registered with Axon server ? I don’t see any configuration in these services.
I saw that you mentioned they get registered using service discovery mechanism. But how does that work ? Will axon server registers all the Spring boot application running in the system ?

    Saurabh Dashora · May 22, 2020 at 2:16 am

    Hi Mouli,

    Actually the Axon dependency we include is configured to connect to a running instance of the Axon Server on a particular port. So as soon as we start our application, it registers with the Axon Server. If the dependency is not there in our Spring Boot application, it will not register.

Sreeni · June 23, 2020 at 6:03 pm

Nice place to start Data consistency using SAGA & Event sourcing ( AXon server) for Microservices

    Saurabh Dashora · June 24, 2020 at 2:09 am

    Thanks for the nice feedback Sreeni!

Wasiq · December 17, 2020 at 11:23 am

Thankyou so much for this wonderful effort.it would be great if you make one example with apache kafka as well to ensure data consisteny with sagas pattren.
Thanks once again for this simplified example.

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

    Hi,

    I’m glad you liked the post!

Rowland · January 13, 2021 at 7:32 pm

Nicely presented example Saurabh. I think that your explanation could benefit from some added detail about failure recovery and duplicate command handling. For example, I think that in a robust implementation, the Payment service would need to be able to recognize duplicate CreateInvoiceCommands for the OrderId and ignore the second command. Without this capability, customers could be invoiced twice for the same order.

    Saurabh Dashora · January 18, 2021 at 1:46 am

    Hi Rowland…yes I agree though couldn’t quite get around to implementing those features…will target those in a future post.

Leave a Reply

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