Applying a Microservice Architecture to Enterprise Applications – Part 1

This post describes highly scalable architectures based on small modules aka “microservices”. The microservices architecture allows for fine-grained scaling of operations where every single module can be scaled as required without it affecting the remainder of the system. It allows for better Continuous Integration/Continuous Deployment by allowing every part of the system to evolve and be deployed independently.

Microservices are more than scalable components. They are building blocks that can be developed, maintained and hosted independent of each other. Splitting development, deployment, and maintenance improves the system’s overall CI/CD cycle.

Independence and fine-grained scalability are in the very nature of microservices. This leads to following principles.

Independence of design choices: The design of one microservice must not depend on the design choices that were made in the implementation of other microservices. This gives us liberty to use technologies that are best fit to solve the technical challenges specific to each microservice.

A consequence to this principle is that different microservices cannot connect to same shared storage since sharing the same storage also means sharing all the design choices that determined the structure of the storage subsystem. Thus, either a microservice has its own data storage or it has no storage at all. In the later case, it has to communicate with other microservices that take care of data storage.

This doesn’t mean that every microservice microservice would always have a dedicated storage. Some complex domains can have a microservice (logical microservice) made up of multiple microservices (physical microservices). Multiple physical microservices may access the same database that serves the logical microservice.

Independence from deployment environment: Microservices are scaled out on different hardware nodes and different microservices can be hosted on the same node. Therefore less a microservice relied on the operating system and other software, more available hardware nodes it be deployed on. This is the reason microservices are usually containerized. Containerization allows each microservice to bring its dependencies along so that it can run anywhere.

Loose Coupling: Each microservice must be loosely coupled from other microservices. This also means that each microservice must be able to perform its operations independently and should not introduce “chatty communication” with other microservices.

No Recursive Request/Responses: Microservices must not cause recursive chain of nested request/responses to other microservices. Nested requests can degrade response time. Usually, nested requests are signs on interdependency. Such interdependency can be avoided if each microservice stores all the data that it needs to ensure fast responses. To keep their data up to date for incoming requests, microservices must communicate their data changes as soon as they occur with microservice. This is generally achieved through asynchronous messages since synchronous nested messages can cause thread starvation and degrade responses.

Resiliency: Fine-grained scaling of distributed microservices that communicate with each other through asynchronous communication requires each microservice to be resilient. Communication initiated by or directed to specific microservice may fail due to transient errors or network failures. Such failures can be temporary or persistent in nature. Temporary failures can be handled with appropriate retry mechanisms. Retries in case of persistent failures, retries can cause an explosion of retry operations leading to saturation of server resources. This requires retry with back-off strategies to ensure that failures to one system do not propagate to other systems also called as – congestion propagation.

The preceding principles and constraints are best practices for building enterprise applications using microservices based architecture.

In the next post, we will discuss more on patterns and practices for avoiding recursive request/responses and implementing resiliency in enterprise applications.

Leave a comment