Microservice Design Patterns

 Microservice Design Patterns 


Design Patterns helps you to resolve complex issue and avoid redundancy in code base and important thing is it helps you to understand Data flow between services and avoid Object error state .


Most common Microservice Design Patterns are -

#1. API Gateway Pattern

      This pattern creates a single access point for client requests, directing them to the relevant microservice also Known as (BFF - Backend for Frontend)




A variation of this pattern is the Backends for frontends pattern. It defines a separate API gateway for each kind of client.




Example - Netflix API Gateway , JAVA/Spring API gateway.


#2. Bulkhead Pattern

This pattern isolates failures in a microservices architecture by placing each microservice in a separate container to prevent one failure from impacting others.

In a bulkhead architecture, elements of an application are isolated into pools so that if one fails, the others will continue to function

The below  diagram shows bulkheads structured around connection pools that call individual services.

 If Service A fails or causes some other issue, the connection pool is isolated, so only workloads using the thread pool assigned to Service A are affected. Workloads that use Service B and C are not affected and can continue working without interruption.



       

 # 3. Circuit Breaker Pattern (Fault Tolerance Microservice)
      
 This pattern addresses failures in microservice architectures by rerouting requests to an alternative service when a microservice is unresponsive.

It allows developers to prevent cascading failures in microservices architecture by invoking remote services through a proxy. For example, this pattern works the same as the electric circuit breakers at your home.

The Circuit breaker design patterns have 3 states-
   a) Closed
   b) Open
   C) Half -Open




#4. CQRS Pattern (Command Query Responsibility Segregation )

This pattern separates read and write models in a microservices architecture, optimizing the read model for data querying and the write model for data updating.

 We can use CQRS design pattern in order to avoid complex queries to get rid of inefficient
 joins basically this pattern separates read and update operations for a database.


Normally, in monolithic applications, most of time we have 1 database and this database should respond both query and update operations. That means a database is both working for complex join queries, and also perform CRUD operations. But if the application goes more complex this query and crud operations will be also is going to be un-manageable situation.

In example of reading database, if your application required some query that needs to join more than 10 + table, this will lock the database due to latency of query computation. Also if we give example of writing database, when performing crud operations we would need to make complex validations and process long business logics, so this will cause to lock database operations.

So reading and writing database has different approaches that we can define different strategy to handle that operation. In order to that CQRS offers to use “separation of concerns” principles and separate reading database and the writing database with 2 database. By this way we can even use different database for reading and writing database types like using no-sql for reading and using relational database for crud operations.

Another consideration is we should understand our application use case behaviors, if our application is mostly reading use cases and not writing so much, we can say our application is read-incentive application. So we should design our architecture as per our reading requirements with focusing reading databases.

So we can say that CQRS separates reads and writes into different databases, Commands performs update data, Queries performs read data.





#5. Event-Driven Architecture Pattern 

This pattern uses events as a means of communication between microservices, allowing them to publish and subscribe to each other's events.

These architectures are comprised of three critical features:

  1. Event producers, which detect and send events to routers when they occur,
  2. Event routers, which analyze events and determine where to send them, and
  3. Event consumers, which process events, handle them, and complete the action (e.g., updating a data warehouse to reflect current inventory levels

An event-driven architecture ensures applications can quickly respond to state changes as users, bots, and webhooks perform specific actions. In today’s age of ubiquitous connectivity and lightning-fast networks, it comes as no surprise that more and more engineering teams are using event-driven architectures to construct their applications.


Note - This pattern has been deprecated and replaced by the Saga pattern.


#6 . SAGA Pattern 

This pattern manages transactions across multiple microservices by breaking them into smaller steps and utilizing compensating actions to reverse completed steps in case of errors.

Saga architecture pattern provides transaction management using a sequence of local transactions.

A local transaction is the unit of work performed by a Saga participant. Every operation that is part of the Saga can be rolled back by a compensating transaction. Further, the Saga pattern guarantees that either all operations complete successfully or the corresponding compensation transactions are run to undo the work previously completed.

In the Saga pattern, a compensating transaction must be idempotent and retryable. These two principles ensure that we can manage transactions without any manual intervention.

The Saga Execution Coordinator (SEC) guarantees these principles




Lets discuss about  Saga Execution Coordinator (SEC)

The Saga Execution Coordinator is the central component to implement a Saga flow. It contains a Saga log that captures the sequence of events of a distributed transaction.

For any failure, the SEC component inspects the Saga log to identify the impacted components and the sequence in which the compensating transactions should run.

For any failure in the SEC component, it can read the Saga log once it’s coming back up.

It can then identify the transactions successfully rolled back, which ones are pending, and can take appropriate actions.





 There are two approaches via we can implement Saga Patterns -
    1. Choreography
    2. Orchestration

 

Saga Choreography Pattern:

   In the Saga Choreography pattern, each microservice that is part of the transaction publishes an event that is processed by the next microservice.

To use this pattern, we need to decide if the microservice will be part of the Saga. Accordingly, the microservice needs to use the appropriate framework to implement Saga. In this pattern, the Saga Execution Coordinator is either embedded within the microservice or can be a standalone component.

In the Saga, choreography flow is successful if all the microservices complete their local transaction, and none of the microservices reported any failure.

The following diagram demonstrates the successful Saga flow for the online order processing application.




In the event of a failure, the microservice reports the failure to SEC, and it is the SEC’s responsibility to invoke the relevant compensation transactions.



in this example, the Payment microservice reports a failure, and the SEC invokes the compensating transaction to unblock the seat. If the call to the compensating transaction fails, it is the SEC’s responsibility to retry it until it is successfully completed. 


The Choreography pattern works for greenfield microservice application development. Also, this pattern is suitable when there are fewer participants in the transaction.



Saga Orchestration Pattern

In the Orchestration pattern, a single orchestrator is responsible for managing the overall transaction status. If any of the microservices encounter a failure, the orchestrator is responsible for invoking the necessary compensating transactions.



The Saga orchestration pattern is useful for brownfield microservice application development architecture. In other words, this pattern works when we already have a set of microservices and would like to implement the Saga pattern in the application. We need to define the appropriate compensating transactions to proceed with this pattern.

Here are a few frameworks available to implement the orchestrator pattern:

  • Camunda is a Java-based framework that supports Business Process Model and Notation (BPMN) standard for workflow and process automation.
  • Apache Camel provides the implementation for Saga Enterprise Integration Pattern (EIP).


#7. Service Mesh Pattern

This pattern adds an infrastructure layer between microservices to handle concerns such as load balancing, service discovery, and security.

Service mesh is a technology pattern that can be applied to a microservice-based system to manage networked communication between services. With a service mesh, the networking functionality is decoupled from the service's application logic, which means it can be managed independently.

A service mesh is a dedicated infrastructure layer for handling service-to-service communication. It’s responsible for the reliable delivery of requests through the complex topology of services that comprise a modern, cloud native application. In practice, the service mesh is typically implemented as an array of lightweight network proxies that are deployed alongside application code, without the application needing to be aware.

each of your services will have a companion proxy sidecar. Given that services communicate with each other only through the sidecar proxy, we end up with a deployment similar to the diagram below.


#8 Service Registry Pattern 

This pattern maintains a central directory for service discovery, tracking all services in a microservices architecture.

in a Microservices based application, we have many instances of the several services on the different server. Due to dynamically changes in the ips and port numbers of these services, it is much more difficult to manage service discovery operations.



we can say that The Service Registry is a database for microservice instances. When the client service need to access internal services, then it will query from Service Registry and access them.

Example - Netflix Eureka 

nowadays, this pattern is not require to implement because of the container orchestrator systems automatically handle to service discovery operations itself. For example Kubernetes has Service definitions that basically perform all these task after definition of services on K8s. So its not a big deal now, but its good to know underlying pattern of this Microservice Discovery Patterns and Service Registry operations
                                                                                                                                       

#9 Sidecar Pattern

This pattern deploys an additional container alongside each microservice to handle cross-cutting concerns like logging, monitoring, and security.

Segregating the functionalities of an application into a separate process can be viewed as a Sidecar pattern. The sidecar design pattern allows you to add a number of capabilities to your application without additional configuration code for third-party components.

Benefits of using Side Car -

  • Reduces code duplication in a microservice architecture since you do not need to write configuration code inside each microservice.
  • Provide loose coupling between application code and the underlying platform.




Inside a service mesh, we have the concept of a data plane and a control plane.

  • The data plane's responsibility is to handle the communication between the services inside the mesh and take care of the functionalities like service discovery, load balancing, traffic management, health checks, etc.
  • The control plane's responsibility is to manage and configure the sidecar proxies to enforce policies and collect telemetry.

In the Kubernetes and Istio world, you can inject the sidecars inside a pod. Istio uses the sidecar model with Envoy as the proxy.

Envoy from Lyft is the most popular open source proxy designed for cloud native applications. Envoy runs along side every service and provides the necessary features in a platform agnostic manner. All traffic to your service flows through the Envoy proxy.


# 10 Strangler Pattern

This pattern incrementally replaces a monolithic application with microservices, slowly introducing new microservices while removing functionality from the monolith.

The Strangler Pattern is a popular design pattern to incrementally transform your Monolith application into Microservices by replacing a particular functionality with a new service. Once the new functionality is ready – the old component is strangled, the new service is put into use & the old component is decommissioned all together.




Transform way is :



All patterns in one go -






Thanks ,

Gagan Tyagi

Tech Architect | Tech Mahindra

Follow me on twitter @gagan2u2002

Comments