Microservice Architecture is a variant of the Service Oriented Architecture structural style, which decompose an application into a collection of loosely-coupled services, interacting with each other using communication mechanisms like API.
Microservices aims to provide Scalability, Decentralization, Availability, Isolation from Failures, Real-time Load Balancing etc.
IDEALS Principles incorporates key Principles of Microservice-based Architecture. It includes some of the SOLID principles. IDEALS stand for Interface Segregation : Deployability : Event-Driven : Availability Over Consistency : Loose-Coupling : Single Responsibility
Adhering to these principles poses implementation challenges which are solved with the help of different design patterns.
Design Patterns are reusable solutions to commonly occurring problems within a given context in software design. Context here is microservices architecture. These patterns complement each other catering to different layers and problems in an application's architecture, as illustrated in image below :
Image Details can be referred at : docs.microsoft.com/microservices-design-patterns
Once we decide to Refactor our application from Monolithic to Microservices, first question in our minds is : "So, what's the plan, how we gonna do it ?", as we have an existing function business application in Prod along with multiple new requirements lined up.
One way to do so is via Strangler Fig Pattern. Strangler Fig, also called Strangler is named after any of numerous species of tropical figs (genus Ficus, family Moraceae) named for their pattern of growth upon host trees, which often results in the host's death.
Strangler Fig supports incremental refactoring of a legacy application into a new (strangler) microservice application, by gradually replacing specific pieces of functionality with new services via steady and frequent releases.
It consists of two types of services. One, those which implement functionality that previously resided in the monolith, which shrink over time. Second, services that implement new features.
Another pattern for refactoring is Anti-Corruption Layer which implements a façade between new and legacy applications, to ensure that the design of a new application is not limited by dependencies on legacy systems.
Interface Segregation Principle suggests each microservice interface to attend to a specific need of a client's program. In other words, clients should not be forced to depend on an interface they don't use.
One way to implement the principle is through an API gateway.
API Gateway address the problem of how an individual service is accessed in Microservices-based application by helping understand various communication protocols, translate them as needed, modify the request and response to suit the service and the client programs, etc. API gateway acts as the single entry point for all clients and handles requests by proxying or routing to the appropriate service or by fanning out to multiple services.
Deployability Principle acknowledges critical design decisions and technology choices developers need to make regarding packaging, deploying and running microservices.
- Configuring the runtime infrastructure including containers, pods, clusters, persistence, security and networking
- Scaling microservices in and out or runtime environment migration
- Expediting the commit+build+test+deploy process
- Minimizing downtime
- Synchronizing version changes
- Monitoring microservices health to identify and remedy faults
Each service is deployed as a set of service instances for throughput and availability.
The Single Service per Host and Multiple Services per Host patterns are two different deployment strategies, addressing ways to package and deploy them.
Single Service per Host deploys each single service instance on its own host.
This pattern provides service instance isolation avoiding resource conflicts and making monitoring and management straightforward, but stands comparatively less efficient to Multiple Services per Host in resource utilization.
Multiple Services per Host run multiple instances of different services on a shared host. This could be done by deploying each service instance as a JVM process or by deploying multiple service instances in the same JVM.
There are further refinements to this Single Service per Host pattern like Service Instance per VM Pattern and Service Instance per Container Pattern.
Serverless deployment Pattern is an alternative solution to those mentioned above.
Event Driven Principle
One of the critical design decisions to make microservices fault-tolerant is to avoid cascading failures caused by downtimes of dependent services.
Event-driven Principle helps in this aspect as the producer and consumers of the event are not tightly coupled. The messaging channel used for communication keeps both ends of it independent and hence keeps them loosely coupled. This principle improves reliability, scalability and throughput of the entire system.
Event Sourcing is a design pattern implementing this principle, which helps reliably, atomically update the database and send messages/events. For example, a service that participates in a saga (explained below) needs to atomically update the database and sends messages/events. Similarly, a service that publishes a domain event must atomically update an aggregate and publish an event, which can be done implementing Event Sourcing.
Availability over Consistency
The CAP theorem essentially gives two options: Availability OR Consistency, along with partition tolerance.
For microservices, the main strategy that enables the availability choice is data replication, which can be achieved via Service Data Replication pattern, Command Query Responsibility Segregation (CQRS) pattern etc. CQRS separates the design and implementation of operations that change data (commands) from the ones that only read data (queries).
Message replication through Event Sourcing Pattern (event-driven architecture) generally works on Eventual Consistency, keeping Availability over Consistency.
Loose Coupling is a coupling technique used to describe the degree and intent of interconnected but non-dependent components within an information system. Loosely coupled with other services - enables a team to work independently the majority of time on their service(s) without being impacted by changes to other services and without affecting other services.
Several strategies can be used and combined to promote afferent (incoming) and efferent (outgoing) loose coupling. Afferent coupling is a measure of how many other services use a specific service as Incoming and Efferent coupling is a measure of how many different services used by the specific service as Outgoing.
Single responsibility is the idea that enables modeling microservices that are not too large or too slim because they contain the right amount of cohesive functionality.
Few Design Patterns promoting loose coupling are Database per Microservice, Saga, API Gateway, BFF, Saga etc.
Backends for Frontends (BFF) creates separate backend services for different types of clients, such as desktop and mobile. That way, a single backend service doesn't need to handle the conflicting requirements of various client types. This pattern can help keep each microservice simple, by separating client-specific concerns.
Database per Microservice suggests each microservice’s persistent data private to that service and accessible only via its API. A service’s transactions only involve its database.
Here, tight coupling is being handled at database layer.
Separate data store doesn't imply only a database for each service, rather Private-tables-per-service, Schema-per-service or Database-server-per-service, as per requirement.
When we implement Database per Microservice, some business transactions span multiple services, hence need to implement transactions that span services arises, which is addressed yet another patter known as Saga Pattern.
Saga is a long lived transaction that can be broken up into transactions, but still executed as a unit, ensuring eventual consistency.
Few other database related patters are API Composition and Command Query Responsibility Segregation (CQRS) pattern which provide ways to implement queries.
Single Responsibility Principle (SRP) is about having cohesive functionality in an OO class. The notion of single responsibility can be extended to the cohesiveness of services within a microservice. The microservice architecture style dictates that the deployment unit should contain only one service or just a few cohesive services. If a microservice is packed with responsibilities, that is, too many not quite cohesive services, then it might bear the pains of a monolith.
Here, we have seen only a bunch of design patterns with only few microservices principles. It is wider topic which cater to variety of domains, use-cases and challenges.
Image References :