Despite the merits of microservices, there are many challenges that you can face when implementing them.
The study of common patterns in solving these problems led to the emergence of microservice design patterns. The main goal is to provide time-tested solutions for such tasks as developing a microservice architecture and organizing the interaction of microservices with each other, client applications, databases, ensuring their fault tolerance. Let’s consider several basic patterns, dividing them into conditional groups depending on the problem being solved.
Decomposition Patterns Into Microservices
This block of templates offers solutions for decomposition, that is, splitting applications into microservices.
Decompose By Business Capability Template
One of the most well-known ways to break down into microservices is to define the business capabilities of the application and create one microservice for each of them. Business Opportunities are functions that will be available to users while using the application.
Decompose By Subdomain Template
When broken down by business opportunities, so-called “God Classes” may appear – entities that will be common to several microservices.
For example, in an application for an online store, such an entity can become an order. The model above is used in several services: Orders Creation, Orders Delivery, Orders Alerts, Preorders.
To avoid the appearance of God Classes, you can use an alternative pattern of decomposition into microservices – partitioning into subdomains. It is based on Domain-Driven Design (DDD) concepts.
DDD breaks down the entire domain model (domain) into subdomains. Each subdomain has its data model, the scope of which is usually called a bounded context. Each microservice will be developed within this bounded context. The main task of using the DDD approach is to select subdomains and the boundaries between them so that they are as independent as possible.
Let’s go back to the example with an online store. Everything related to orders can be considered within the Orders Subdomain, and it is within this subdomain to create a microservice for managing orders (Orders Service). Thus, you can reduce the number of microservices compared to decomposition based on business opportunities. In our highly simplified example, four microservices have been transformed into one.
Refactoring Patterns For Migrating To Microservice
This group of templates is intended for organizing interaction with Legacy applications and their gradual transition to a microservice architecture.
- The microservice partitioning methods discussed above are well suited for new applications that are built from scratch. However, in practice, it often becomes necessary to transfer existing monolithic applications to a microservice architecture. Decomposing a monolith into microservices takes time and cannot be done in a single iteration. Therefore, the Strangler pattern was developed, named by analogy with the liana, which gradually strangles the tree it wraps around.
- This pattern means migrating a monolithic application to a microservice architecture by gradually migrating existing functionality to microservices. The facade intercepts the client’s request and directs it to the microservices when the following functionality is transferred from the monolith to microservices. At the same time, new functions are implemented exclusively in microservices, bypassing the monolith. After the transfer of all parts, the monolithic application is completely decommissioned.
- The pattern is not recommended for small monolith sizes. In this case, the best solution would be to switch it to a microservice architecture since adding a facade increases delays and makes testing difficult.
Anti-Corruption Layer Template
When migrating Legacy applications to microservices, refactoring some subsystems can be very time-consuming or even impossible. But you still need to interact with outdated subsystems, even though they may not use the most modern technologies in building APIs, data schemas, etc.
For such cases, the Anti-Corruption Layer pattern is excellent. It is designed to isolate various subsystems by placing an additional layer between them, which can be implemented as an application component or an independent service. This level connects the two subsystems, allowing them to remain as independent of each other as possible. It contains all the logic necessary to transfer data in both directions: when interacting with each of the subsystems, its data model is used.