Mar
08
2019

Strangling a monolith with Axon Server

Transforming large legacy monoliths into decoupled, scalable microservices systems is more complex than can be imagined. This blog describes how using Axon Server with the strangler pattern can help simplify this process and speed up the delivery time. Please consider this is general advice that, whilst applied in many cases, requires careful consideration before using it in your particular scenario.

In the past, software applications were built as monoliths until the Service-oriented Architecture approach became popular. Nowadays, many monoliths are still maintained and built because they represent valuable business logic.

Monoliths are easy to deploy and can have a good performance because of the usage of shared memory. Still, there are several reasons to move away from this way of working to a microservices approach:

  • if the application has evolved into a big ball of mud
  • tight coupling forces you to test and deploy the whole application when adding features
  • when the application has become too large and too complex to be able to do automated regression tests
  • when starting up, the application becomes very slow

In monoliths (which have not become a big ball of mud), you can distinguish between several functional parts as shown in this simplified example of an e-commerce system:

Simplified example of an e-commerce system in which several parts are identified: Profile & Account Management, Product Catalog Management, Order Management, Cart Management (with a Payment Serivce that links to an external Payment Service Provider), Shipping and Suppliers Management

When a new system is built up from scratch, it is easy to set up a microservices landscape, but there is the burden of an existing system in most cases. To build a copy of the “old system” from scratch costs a lot of money because, for a long period, two systems have to be maintained, and therefore, another approach is desired. This is where the strangler pattern (described by Martin Fowler) comes in: the monolith is not replaced in one high-risk big-bang release but incrementally. Features are replaced piece by piece by microservices, slowly strangling the monolith.

Services in a monolith are called directly, and therefore adding a new microservice for that service adds boilerplate code to your monolith as well as to your new microservice (calling functionality in another service with REST or AMQP). This should happen for every call, and you’ll end up with many pointers from and to services. It’s tempting to create dependencies between services, and when times go by, you could end up with a distributed monolith.

Another challenge is service discovery in a microservice environment. A developer should not worry about where to find the service; it should be location transparent. Axon Framework and Axon Server minimize the lines of code needed for communication between services so that the developer can focus on the business logic.

The first thing that needs to be done is adding Axon Framework to your monolith (or creating a microservice as an interface) to enable communication between the monolith and Axon Server. This implies adding a Command Bus, Query Bus, and Event Bus to send messages.

After having Axon Server installed, it’s recommended to extract a completely independent part of the monolith. So, for the e-commerce example, we’ll zoom into the payment service. The payment service is the interface between the cart and the (external) Payment Service Provider.

So, for the e-commerce example, we’ll zoom into the payment service. The payment service is the interface between the cart and the (external) Payment Service Provider. The axon Framwork is added and used by the Cart Management part in the Monolith, The Payment service now is extracted as an independent module that still uses the external Payment Service Provider. The Cart Management, using Axon Framework  sends messages to Axon Server, and the new extracted payment Service, receives those messages from Axon Server. Messages could be commands, queries and events.

The next step is to create a microservice that implements all the payment logic supported by the monolith. First, find the border between the monolith and the payment logic. Sometimes the border is not clear, and you might need to create a sharper border separating concerns into account. Next, find all the commands that should be done at this border, for example, PayOrderCommand or RefundOrderCommand. These commands are sent via Axon Server to the new microservice. The payment service handles the commands and will send Events like OrderPayedEvent or PaymentCancelledEvent. When the payment service needs extra info, e.g., the customer's name, it can send a CustomerInfoQuery via Axon Server. In the monolith in the Axon Framework Interface, you can provide this information by implementing a Query Handler. Later on, when maintaining account information is moved to a separate service, the query can be handled.    

By repeating the process of isolating and extracting functionality, all features can be moved to a microservice, and the monolith can be decommissioned:

By repeating the process of isolating and extracting functionality from the monolith, all features can be moved to a microservice, and the monolith can be decommissioned: In this step, in addition to the payment service, also Cart Management and order Management have been extracted from the monolith as independent microservices communicating through axon Framework and Axon Server.
By repeating the process of isolating and extracting functionality from the monolith, all features can be moved to a microservice, and the monolith can be decommissioned: In this step, in addition to the previous microservices, also Shipping and Suplpiers management parts have been extracted from the monolith as independent microservices communicating through axon Framework and Axon Server.
By repeating the process of isolating and extracting functionality from the monolith, all features can be moved to a microservice, and the monolith can be decommissioned: In this step, aall the pending parts have been extracted from the monolith as independent microservices communicating through axon Framework and Axon Server. The monolith now is empty.

In the end, the empty monolith and temporary interface with Axon Server can be removed:

In the end, the empty monolith and temporary interface with Axon Server can be removed:

It’s not necessary to do this whole decomposition; there’s always a choice to leave some parts of the monolith as-is.

Conclusion

When the decision is made to transition from an existing monolith to a microservice solution, the strangler pattern is an agile approach to accomplish this. Using Axon Server for this exercise makes service location transparent and diminishes the number of lines of code needed for connecting.

Axon Server is a zero-configuration message router and event store for Axon-based applications. You can download the quick start package here.


Written by:
Yvonne's profile photo

Yvonne Ceelie

A backend developer with more than 2 decades of experience and passionate about Java and the Spring Framework. Since 2017 my main focus is helping customers of AxonIQ to use and implement the Axon Framework as a consultant. I like to solve complex puzzles as easy as possible.


September 28th, Amsterdam

Join us for the AxonIQ Conference 2023, where the developer community attends to get inspired by curated talks and networking.

September 27th, Amsterdam

The event to collaborate, discuss, and share knowledge about techniques, tools, and practices for building complex, distributed event-driven applications.