Microservices Design Pattern

Microservices design patterns are software patterns that generate reusable autonomous services. There are numerous design patterns for microservices to choose from, each with specific advantages and drawbacks, and the best pattern (or patterns) to use will depend on business needs and other related factors.


Article content

Let’s now look at some of the most common examples of microservices design patterns and when they should be used by developers.

Integration patterns

The most commonly used microservices design patterns for integration are API Gateway and Backend for Frontend.

API gateway

The API gateway microservices pattern is designed to solve the problems that can be caused by decomposing an application into separate services. These can include issues with requesting information from multiple microservices or handling several protocol requests simultaneously.

The API gateway design pattern can be considered a proxy service. It acts as the entry point for all microservices and routes a request to the correct service or services. The results can then be sent back to the composite or consumer service. By using an API gateway, microservices can communicate with each other via a stateless server, for example using either HTTP requests or a message bus.


Article content


In addition, a microservices API gateway can expose the APIs specific to each client service and create a more tailored user experience. It can also offload the responsibility for microservices’ authentication and authorization. In this way, it can provide an extra layer of security for your sensitive data.


Backend for frontend

Backend for frontend, also known as BFF, is a variant of the API gateway design pattern that provides an extra layer between client applications and microservices.

Unlike the API gateway pattern, BFF isn’t a single point of entry. Instead, it introduces a separate gateway for each client. With this approach, you can add an API tailored to specific requirements.

For example, a company might have a web application, a mobile application, and a third-party application. With the BFF pattern, a separate API can be added for each app instead of a single bloated API being used for all three. This not only improves user experience but also enhances system performance as each app can call the backend in parallel.


Cross-cutting concern patterns

Although microservices aim to allow applications to operate independently, there can still be issues with cross-cutting concerns. Microservice design patterns like blue-green deployment, the circuit breaker pattern, and service discovery can help mitigate these concerns.

Blue-green deployment

A blue-green deployment strategy can reduce the risks of latency and downtime caused by multiple microservices falling under the umbrella of one application.

This pattern involves always running two identical production environments: blue and green. Only one of these environments is live at any time, serving all production traffic. When a developer is ready to upload a new version of the service, they can upload it to the inactive environment. This way, developers can perform any necessary tests without disrupting service.

Once the software is ready, the live environment is switched to the new version. The old version becomes inactive but remains operational. If the new version experiences any problems, the service can be switched back to the older version.


Article content


Circuit breaker pattern

Microservice architectures involve a lot of calls between applications and services. If a service has failed, the unanswered calls can use up important resources and create a cascade of failures across the system. The circuit breaker pattern provides protection against this possibility.

Inspired by electrical circuit breakers, this microservice design pattern breaks the connection to failed services. Any calls to the breaker are routed to a different service or result in an error default message, preventing protected calls from being made and left ‘hanging’. This happens for a set ‘timeout’ period.


Service discovery

When it comes to container technology, IP addresses are allocated to service instances. This means each time an address changes, consumer services may break and will require manual adjustments.

A service database, known as the Service Registry, is created to store the metadata for each producer service and specification. A service instance should register to the Service Registry when starting and also de-register if shutting down.

There are two types of service discovery, each with pros and cons:

  • Client-side: the client queries the Service Registry directly to find the location of a service instance. This model reduces the number of network hops needed. However, you must implement service discovery logic separately for each language or framework your application uses, increasing the complexity of the client code.
  • Server-side: the client queries the Service Registry indirectly via a load balancer. In this model, the client code is simpler, but there are more moving parts to maintain and monitor. More network hops are also needed, which may introduce latency.

Data management patterns

Since each microservice is autonomous, each service has a separate database. This can create problems if applications need to call more than one service. To manage data effectively, you need to implement one or more data management design patterns.

CQRS design pattern

The command query responsibility segregation (CQRS) pattern can be useful if you have a large application reading data from an event store. It’s often used alongside the event sourcing pattern.

The CQRS pattern separates the ‘read’ and ‘update’ operations. This separation of concerns allows the software development team to adapt models that are more manageable and offer greater flexibility. The flexible nature of this design pattern can also be beneficial for systems that evolve over time.


Event sourcing pattern

When dealing with requests, microservices need to exchange data. For stable, highly scalable systems, they should communicate asynchronously by exchanging ‘events’.

In some conventional databases, the business entity with the current ‘state’ is directly stored. With event sourcing, any state-changing (or significant) events can be stored in place of entities. This means changes are saved as a series of immutable events.

The state of a business entity can be deducted by reclaiming all the events in it. Different services can replay events from the event store to determine the correct state of their individual data stores (since data is stored as a series of events rather than by making direct updates to stores).


Saga design pattern

One of the biggest problems with microservices architecture is how to work around transactions that span multiple services. The saga pattern can help with this.

Saga allows developers to manage eCommerce transactions across multiple microservices using a sequence of local transactions. Each of these is accompanied by an event that will trigger the next stage.

As part of this approach, if one transaction fails, a rollback transaction is triggered to compensate.


Observability patterns

As we’ve mentioned previously, continuous monitoring is one of the cornerstones of microservices architecture. Observability patterns like the ones below can help you achieve this.

Distributed tracing

Microservices are the intersection of DevOps. In a microservice architecture, requests may span multiple services. Each service deals with a client request by performing one or more operations across multiple services. This can make troubleshooting difficult, as it’s hard to track end-to-end requests.

Distributed tracing is one solution to this problem. With this pattern, a distributed tracer gives each request a unique ID. It also records information about the requests, such as which services are called and which operations are performed.


Log aggregation

Each microservice generates a standardized log file about its activities. This can be useful in cases where an application may consist of several services. Requests often require multiple service instances.

However, there needs to be a centralized logging service that can compile logs from each service instance. This is where log aggregation comes in: it normalizes and consolidates logs from different microservices and stores them on a centralized platform.

Developers can search and analyze logs on the platform. They can also create alerts that are triggered when certain problem messages appear, simplifying issue resolution.

Performance metrics

It’s important to keep an eye on transactions so that patterns can be monitored and problems identified. However, the increased service portfolio of microservices architectures can make this difficult.

With the performance metrics pattern, you can gather data about individual operations (such as latency and CPU performance) and consolidate it. The pattern aggregates the metrics of different services into a single metrics service that offers reporting and altering capabilities.


Decomposition patterns

Microservices decomposition is the process of breaking down applications into smaller independent services. This must be done logically, so it’s wise to implement one of the following decomposition design patterns.

By business capability

‘Business capability’ generates value for a business. The particular mix of business capabilities depends on the nature of the enterprise.

For instance, the capabilities of an online tech sales company include sales, marketing, accounting, etc. Each business capability may be thought of as a service (but one that’s business-oriented as opposed to technical).

To decompose an application into smaller services, it can be beneficial to use the business capability pattern

This pattern results in a stable microservices architecture as business capabilities are generally stable. However, its success relies on the ability to identify each specific business capability.


By subdomain

Domain-driven design (DDD) can resolve the God Class issue by using subdomains and bounded context concepts.

This pattern starts by breaking the domain model into subdomains, such as ‘core’ and ‘supporting’ activities or features. Each subdomain has a model with a scope known as the ‘bounded context’. Microservices are developed based on this.

This pattern is ideal for decomposing complex applications and supporting agile development. It’s also a great way to increase the flexibility and scalability of your architecture.


By transactions

Another pattern involves decomposing via transactions. With this pattern, each transaction and its supporting components belong to a single microservice. For instance, you could create an order management microservice and a payment processing microservice.

This pattern solves many problems that arise when transactions span multiple services, like increased latency and complexity. This in turn can simplify the process of developing, testing, and maintaining the system.

On the other hand, this pattern has the potential for an excessive proliferation of microservices that may actually increase complexity. So, developers must carefully consider the scope of each transaction and whether decomposing with this pattern is feasible.

Sidecar pattern

During the sidecar pattern (also known as the sidekick pattern), components of an application are positioned in a separate processor container. This provides isolation and encapsulation, which aids fault isolation.

You can use this pattern to encapsulate an app’s supporting functions, like monitoring and configuration, or it can enable applications to be composed of heterogeneous components and technologies.

The sidecar is attached to a ‘parent application’ and provides assisting features for this. It also shares the same lifecycle as the parent since it’s created and retired alongside it.

Bulkhead pattern

The bulkhead pattern is so named because it resembles the sectioned partitions of a ship’s hull. It works by isolating elements of an application into separate sections. If one fails, the others can continue to function.

These partitions are created based on consumer load and availability. It helps to isolate failures, allowing you to keep a service functioning for some customers, even while others are experiencing failure.

This can be a useful pattern for businesses that receive large volumes of customers at once. It can also be combined with the circuit breaker pattern for enhanced fault isolation.


To view or add a comment, sign in

More articles by Punatarr Akash

Others also viewed

Explore content categories