Microservices design patterns in Axon Framework
It's often easier to learn from the real-life perspectives of users than from solutions providers. This is very true for microservices design patterns. At our 2020 conference, a customer team discussed both the theory and their reality of implementing an application using Axon Framework microservice design patterns.
During this session, the team explained why they chose to build the application using microservices with Axon Framework after having compared it with three other architectural styles: Microkernel, Service-Oriented Architecture (SOA), and typical microservices.
Architectural characteristics
To make a fair comparison, the team scored and measured the four different approaches based on eleven architectural characteristics grouped into three categories with as little bias as possible. We can see those in Figure 1.
Figure 1: Categorization of architectural characteristics
Deciding which architectural characteristics were important was an intense, iterative process for the team. Architectural characteristics influence which trade-offs will be made in the applications’ architecture. If there are too many trade-offs, it becomes confused and fails to perform adequately.
Microkernel architecture
A microkernel architecture has a core system at its center, with plugin components providing additional functionality. It’s quick to write and low-cost since it’s typically a monolithic application, consisting of a single service with a single database. It also provides for the ability to partition domains into the plugin components.
Figure 2: An overview of the Microkernel architecture
However, individual components cannot be scaled, and the core system is prone to bloating. It’s also difficult to build fault tolerance into MicroKernel. Therefore, it’s often considered suitable for only small applications or larger applications where less performance is required.
Service-oriented architecture
Service-oriented architecture (SOA) divides a system into multiple coarse-grained services. These are often defined by the domain they represent. For example, for a banking application, there may be a ‘lending service’ and a ‘savings service’.
Since they are coarse-grained and represent a domain, developers can quickly and easily start by creating a shared understanding of the domain. It also encapsulates volatility well, and it has less traffic between services, leading to less latency.
However, the services are easy to get bloated, as are their databases, since most of the time, the services (or the components of a service) share it.
Overall, it’s a good approach in most cases. You can decompose the services into smaller ones if certain desired characteristics, such as performance, require the services to be scoped differently.
Figure 3: An overview of the Service-oriented Architecture
Typical Microservices
The typical microservices design pattern that many companies start out with has lots of small services. The microservices could be scaled effectively at crucial points in the architecture. However, without a framework, they become hard to manage. The data isolation is good, and the architecture allows developers to parallelize work.
Figure 4: An overview of the typical microservices architecture
The challenge of using this approach is that it introduces an API gateway that knows a lot about the individual microservices and their data structure. Services can easily expose the structure of their databases when communicating, leading to accidental coupling between services. These additional network calls also introduce latency, especially when a microservices must call another service to ask for additional information or execute an action.
A platform system is needed to automate discovery, logging, alerting, and other vital concerns to use microservices design patterns efficiently. The microservices approach also introduces a ripple effect when building new features. When building a new feature or enhancing a current one, it’s often needed to change multiple services simultaneously.
Decomposing a service is usually more effective if left until the need arises. Decomposing at the start, as the microservices approach encourages, can cause the services to be scoped incorrectly since the domain is not yet clear.
Microservices with Axon Framework
CQRS and Event-Sourcing are principles most easily applied with the support of a purpose-built framework. Axon Framework removes a lot of friction points within this architecture, and the event-sourcing within the framework is very suitable for a system of record like a bank.
The downside of the approach is that CQRS has a steep learning curve in the beginning and that good aggregate design is complex.
Axon Framework heavily encourages DDD approaches and facilitates volatility encapsulation of projections by providing projections and events. It also provides a lot of the needed boilerplate needed for the architecture, such as message delivery and location transparency.
Figure 5: Overview of the Axon CQRS architecture
Comparison
Based on the key architecture characteristics, the team compared the four microservices design patterns using a scoring system for each of the main architectural categories. Their ratings can be seen in figure 6. Based on this, they found that Axon Framework had the most significant competitive advantage of the four architectural styles and proceeded to implement the application using the microservices design patterns architecture based on Axon Framework.
Figure 6: Rating of the different architectural characteristics
In addition to the comparison, the team also shares their post-implementation experience; it’s a great talk to watch if you are considering Axon Framework for your architecture.
The presentation is available to watch in full on the AxonIQ YouTube channel.