Strangling a Monolith With the Axon Server
Slowly introducing microservices into your application's architecture can be an effective way to break down, or 'strangle,' your monolith.
Join the DZone community and get the full member experience.Join For Free
The process of transforming large legacy monoliths into decoupled, scalable microservices systems is more complex than can be imagined. This blog describes how using the Axon Server with the strangler pattern can help simplify this process and speed up delivery time. Please consider this as general advice which, while it applies in many cases, requires careful consideration before being used in your particular scenario.
In the past, software applications were built as monoliths until the Service-Oriented Architecture (SOA) 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:
When a new system is built up from scratch it is easy to set up a microservices landscape, but, in most cases, there is the burden of an existing system. 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, rather, 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 other services with REST or AMQP). This should happen for every call and you’ll end up with a lot of pointers from and to services. It’s tempting to create dependencies between services but this could cause you to 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. The 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 to add the Axon Framework to your monolith (or create a microservice as an interface) to enable communication between the monolith and the Axon Server. This implies adding a Command Bus, Query Bus, and Event Bus to be able to send messages.
After installing the Axon Server, it’s recommended to extract a completely independent part of the monolith. 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 next step is to create a microservice that implements all the payment logic which is supported by the monolith right now. 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 by taking the separation of concerns into account. At this border, you'll find all the commands that should be done, for example: PayOrderCommand or RefundOrderCommand. These commands are sent to the new microservice via the Axon Server. The payment service handles the commands and will send events like OrderPayedEvent or PaymentCancelledEvent. When the payment service needs extra info, e.g. the name of the customer, it can send a CustomerInfoQuery via the 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 there.
By repeating the process of isolating and extracting functionality all features can be moved to a microservice and the monolith can be decommissioned:
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.
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 the Axon Server for this exercise makes services location transparent and diminishes the number of lines of code needed for connecting to each other.
Opinions expressed by DZone contributors are their own.