As we all know, Microservices brought in a paradigm shift in the way we think, design and architect solutions for a given enterprise need. In a broader context, this new thinking approach provided ways to solve many complex and nagging business challenges in an effective manner. However as a side effect, at the implementation level, we started facing a new crop of problems. One of the greatest human traits is to find a pattern in any problem and come up with standard solutions. Here also we (Microservices community) found solutions for these problems. Hence this leads to a new verbiage ‘Design Patterns in Microservices’.
As a designer/architect, It is important to have a very good understanding of these architectural patterns to come up with an effective microservices driven design/architecture for a given business need. We may end up creating more complexity in the system than needed when we do not understand these patterns. Hence I am going to cover few design patterns which will help us to take better architectural/design decisions which in turn help us to improve the efficiency, ease of scalability and reduce the complexity in a given system.
API Gateway:
Problem statement
A microservice can be consumed by multiple clients such as Web, mobile, other microservices. When we change the URI of the particular microservice, we need to update the same in all clients. All microservices can have common logic like validating the request.
Solution
It defines how clients access the services in a microservice architecture. API gateway is the single entry point for all the clients including other microservices. Whenever we need to change something, we can change at API gateway and no need to change at all the clients. If we want to add/update the request validation or any common logic, we have only one place to tweak it. The API gateway handles requests in one of two ways. Some requests are simply routed/proxied to the appropriate service. API Gateway handles few requests by simply fanning out to multiple microservices.
Circuit breaker:
Problem statement
In the microservices architecture, we will have one or more microservices calling other microservices for data/any other transactions. If a microservice is unavailable due to heavy load or other dependent resources. This can lead to failing the calling microservices and unable to handle any requests. Simply it can be a cascading effect to the all microservices in the application, which are communicating internally.
Solution
A microservice client should call a remote microservice via a proxy service, that acts like an electrical circuit breaker in case of an issue. When there is a failure of consecutive requests of a certain threshold, the circuit breaker trips for a particular duration. During the time all the requests will fail immediately and the circuit breaker will start sending limited requests to pass through. If the requests succeed, then the circuit breaker returns to normal. Otherwise, the circuit breaker will continue to fail the requests for the duration again.
Aggregator:
Problem statement
A microservice can be consumed by multiple clients such as Web, mobile, other microservices. We may need to get the data from different microservices to represent it and we may need to do some data aggregations or apply business logic to it.
Solution
In the Aggregator pattern, a service will invoke multiple microservices to get the data from multiple microservices. Microservice can aggregate the data collected and apply any processing if required. Each microservice can have individual caching & database, in the same way, an aggregator can have it’s own caching to improve the efficiency
Chained:
Problem statement
In eCommerce application, a user wants to purchase a product, then there will be a dependency of order microservice to products microservice.
Solution
Chained microservice design pattern will help us to provide the consolidated outcome to our request. The request received by a microservice-1, which is then communicating with microservice-2 and it may be communicating with microservice-3. All these services are synchronous calls.
Branch:
Problem statement
A microservice may need to get the data from multiple sources including other microservices. When we look at any eCommerce application, those needs get the products data from the catalog, latest pricing, and availability from sellers and any other extra information from the product owner.
Solution
Branch microservice pattern, we can say it is a mix of Aggregator & Chain design patterns and allows simultaneous request/response processing from two or more microservices. The invoked microservice can be chains of microservices. Brach pattern can also be used to invoke different chains of microservices, or a single chain, based our business needs.
Asynchronous Messaging:
Problem statement
In eCommerce application, a user wants to check the product details like pricing as per the seller, expected delivery time and availability. We will be having different microservices for pricing, delivery times & etc… How this data aggregated the shown to the user?
Solution
A microservice can invoke one or more microservices to get the required data. Microservice-1 can call both at microservices 2 & 3 and get the acknowledgement then it will keep on pulling the data from message queue asynchronously. Microservice-2 is responsible to return the pricing based on the seller, one product can be sold by multiple sellers. So it can pull the pricing data from each seller and push to a message queue. Microservice-3 is responsible to return the delivery times based on the locations and it will push the data to message queue.
Database/Datasource per microservice:
Problem statement
In a complete microservices ecosystem, we will end up having multiple microservices and databases. To represent some data we need to fetch from 2 or more different databases. How do we fetch the data from different databases?
Solution
In this pattern, each microservice should have it’s own database/data source. It cannot be accessed directly by other services. Helps ensure that the services are loosely coupled. Changes to one service’s database will not impact any other microservices
Anti-corruption:
Problem statement
Most applications depend on other systems for complete/partial data or functionality. When we migrate a monolithic application to microservices, we need to support the existing behaviour and migrate the application. New microservices need some data from existing services in a different format as the existing system has quality/efficiency issues. How do we take care of the new microservices not compromising its design because of existing services?
Solution
We need to introduce a layer called anti-corruption between existing services and new microservices. This layer will take care/translates the communications between the old and new systems. Also, it allows both systems to remain unchanged without compromising the design.
This will help to migrate a monolithic application to microservices in multiple stages.
In my last blog, I covered an introduction to microservices. This blog covers different challenges that you generally come across while designing the microservices system and tested & proven approaches that you can incorporate at the design stage to address them effectively. After reading this blog, I hope you will have better knowledge on standard challenges and proven solutions in the microservices environment and this information will enable you to make an informed and conscious decision while designing complex microservices based systems.
Good Articles