The process of transforming large legacy monoliths into decoupled, scalable microservices systems is more complex than can be imagined. This blog describes an approach on how using Axon Server with the strangler pattern can help simplify this process and speed up delivery time. Please consider this is general advice which, whilst applies in many cases, careful consideration is needed 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:
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 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 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 and when times goes 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 create 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 be able to send messages.
After having Axon Server installed 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 taking the separation of concerns into account. At this border find all the commands that should be done 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 name of the customer 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 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 do a 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 services location transparent and diminishes the number of lines of code needed for connecting to each other.
Axon Server is a zero-configuration message router and event store for Axon based application. You can download the quick start package here.