Game of Microservices
This article covers some variants of Microservices design patterns and some special considerations. All categories in this article are very short, precise and covers to the point - just like a shooter!
Microservices a paradigm that is one of the most discussed and debated topic in many phases of application development and delivery. The concept had a humble beginning during the time of the aging monolithic architecture which was very sluggish to move at a fast pace. Enterprises found it difficult to deploy monolithic applications at a pace to match the changing business needs or customer requirements. Microservices is an architecture pattern which helps enterprises to move fast and deploy new features at a rapid pace. It leverages small teams allowing them to design, develop and deploy each services atomically as they have full ownership over the life-cycle of their applications.
Microservice architecture gained rapid adoption and greater acceptance due to it's development & deployment pace and maintenance ease. There have been tremendous advancements in this area which will be covered in this article. The below image represents few services which functions together to build a larger e-Commerce application.
Microservices Face-off
There are plenty resources on the internet that speaks about the long-term benefits of Microservices, it's advantages, how it helps organizations/teams to move at a rapid pace in addition with CI/CD pipelines etc. But there are other aspects in this architecture that needs to be addressed and the following sections covers those aspects.
Database per Service
A microservice works best when it has it's own private database (database per service). This ensures loose coupling with other services and the data integrity will be maintained i.e. each microservice controls and updates it's own data. It gives no scope for manual or software systems (applications or automated scripts) that would change data owned by a particular service. All read and write queries for data will be using well defined APIs.
One main point to be highlighted here is, what if a particular microservice has more traffic compared to other microservices? This creates a hot-spot in the application architecture. A hot-spot service is one which handles many requests compared to the other microservices. For example consider a dating application (like Tinder) which is built up of many microservices like, user profile service, photo service, chat service, etc. A typical example of a hot-spot in this example could be the 'User Profile Service'. This is because when many users are online, they are typically browsing or viewing profiles of other users (with profile pictures) and this could yield in couple thousands (or millions) requests per second. This requires the application to be scaled or else it would increase the latency leading to a bad user experience. The 'User Profile Service' can be scaled horizontally but it's database has to be tuned differently to handle additional traffic like sharding, process write requests to primary database and read requests from slave setups, add a cache layer to store most frequently accessed profiles and pictures.
Distributed Transactions & Queries
The next question is, "How to maintain data consistency across different microservices when there are distributed transactions?" In such cases we follow the SAGA-based approach in which services can maintain their data consistency allowing distributed transactions. SAGA-based microservice architecture is covered in the next section.
Another issue is querying for data that resides in two or more microservices. How can this be solved as we understand microservices follow the database per service mandate. There is no option to join tables across databases as we lose data integrity. If the data is stored in different types one service uses an RDBMS while other NoSQL like MongoDB, then it is impossible to join tables and get a consolidated resultset. This limitation is overcome by the CQRS and Event Sourcing approach.
Microservices Based on SAGA
A SAGA is a sequence of local transactions. In SAGA, a set of services work in tandem to execute a piece of functionality and each local transaction updates the data in one service and sends an event or a message that triggers the next transaction in other services.
The architecture for microservices mandates (usually) the Database per Service paradigm. The monolithic approach though having it's own operational issues, it does deal with transactions very well. It truly offers a inherent mechanism to provide ACID transactions and also roll-back in cases of failure. In contrast, in the Microservices approach as we have distributed the data and the datasources based on the service, there might be cases where some transactions, spreads over multiple services. Achieving transactional guarantees in such cases is of high importance or else we tend to lose data consistency and the application can be in an unexpected state. A mechanism to ensure data consistency across services is following the SAGA approach. SAGA ensures data consistency across services.
Consider Figure 1, which is an example of a very high level working of an e-commerce platform. The platform ensures that a when a user (customer) places an order, it will ensure that the payment is initiated and after a successful payment and the order will be delivered. Since Orders, Payments and Delivery services owns different databases the application cannot simply use a local ACID transaction.
Types of SAGA implementations:
- Choreography - An event based pattern to handle distributed transactions. The events are handled asynchronously and the transactions would be completed.
- Orchestration - A command based pattern which has an orchestration mechanism which takes care of delegating commands to dependent microservices.
Choreography
In Choreography based design pattern, every service registers for events with a Queue. Based on the events the corresponding actions will be executed.