Microservice design patterns, how many have you used

Microservice design patterns introduction

Microservice design patterns.Microservice architecture has become a mainstream choice for modern application development. While it solves certain problems, it is not a panacea and has several drawbacks. Hence the need to discuss design patterns for microservices .
Before diving into design patterns, we need to understand the principles of building a microservice architecture:
4.independent, autonomous
5.Decentralized Governance
6.fault isolation
7.Automatic configuration
8.Continuous Delivery with DevOps

However, applying all of these principles presents some challenges and problems. Next, let's discuss these problems and their solutions.
1. Decomposition mode

a. Microservice design patterns.Breakdown by business capability

microservices is to have services loosely coupled and apply the single responsibility principle. However, breaking the application into smaller pieces must be logical. How do we decompose an application into small services?
One strategy is to break it down by business capability—what a business does to create value, and the set of capabilities for a given business depends on the type of business. For example, an insurance company's capabilities typically include sales, marketing, underwriting, claims processing, billing, and more. Each business capability can be thought of as a service.

b. Microservice design patterns.Break down by subdomain
Decomposing an application using business capabilities might be a good start, but you'll come across so-called "God Classes" that aren't easy to decompose. These classes will be common across multiple services. For example, the Order class will be used for order management, order taking, order delivery, etc. How do we break them down?
"Removing the God class " refers to a class that seems to be very functional and difficult to maintain, and assigns its own attributes or methods to their respective classes according to their responsibilities, or decomposes them into classes with clear functions, thereby removing the God class.

Microservice design patterns.For the "God Classes" problem, DDD (Domain Driven Design) comes to the rescue. It uses subdomain and bounded context concepts to solve this problem. DDD will break down the entire domain model that will be created for the enterprise into subdomains, each subdomain will have a model whose scope will be called a bounded context. Every microservice will be developed around a bounded context.
Note : Identifying subdomains is not an easy task. It requires an understanding of the business. Like business capabilities, subdomains are identified by analyzing the business and its organizational structure and identifying different areas of expertise.

c. Microservice design patterns.Strangler Mode

Microservice design patterns.The design pattern we've discussed so far is to decompose greenfield applications (Greenfield), but 80% of the work we do is to deal with brownfield applications (Brownfield), which are large monolithic applications. Applying all the above design patterns to them will be difficult because breaking them into smaller parts is a difficult task.
Greenfield App (Greenfield)
i.e. a new project, starting a new software project from scratch. The analogy is construction on a green space without the need to retrofit or demolish the existing structure.
(from http://en.wikipedia.org/wiki/Greenfield_project )
Brownfield applications
Brownfield development is a term commonly used in the IT industry to describe a site where the existing structure first needs to be demolished, i.e. built within an existing software project.
(from http://en.wikipedia.org/wiki/Brownfield_(software_development) )
This requires the strangler mode ( Strangler ) to the rescue.
strangler pattern is based on the analogy of a vine strangling the tree it is wrapped around. This solution is suitable for web applications where services can be broken up into different domains and hosted as separate services. Doing it one domain at a time will create two separate applications side by side in the same URI space. Eventually, the newly refactored application "kills" or replaces the original application until you can finally abandon the traditional monolithic application.
One of the natural wonders of the [Australia] region is the giant strangler vine. They sow seeds on the branches of the fig tree and work their way down the tree gradually until they take root in the soil. Over the years, they grow into fantastic and beautiful shapes while strangling and killing the tree that serves as their host.
2. Integration Mode
a. API Gateway Mode
When an application is decomposed into smaller microservices , some problems need to be solved:
1.How to call multiple microservices.
2.On different terminal devices (such as PC, APP, and Pad), applications require different data from back-end services because of different UIs.
3.Different consumers may require response data in different formats. Who will do the data transformation or field manipulation?
4.How to handle different types of agreements.
API gateways help solve many of the problems that come with microservice implementations :
1.API Gateway is the single entry point for any microservice invocation.
2.It can act as a proxy service to route requests to related microservices.
3.It can send requests to multiple services and aggregate the results to send back to the requester.
4.A one-size-fits-all API cannot address all consumer needs; this solution creates fine-grained APIs for each specific type of client.
5.It can also convert a request protocol (such as AMQP) to another protocol (such as HTTP) and vice versa.
6.It can also establish unified authentication/authorization for different microservices.
b. Aggregator mode
We've discussed solving the problem of aggregated data in the API Gateway pattern. However, we will discuss it fully here. When decomposing business functionality into several smaller pieces of code, it is necessary to consider how the data returned by each service will be aggregated. This responsibility cannot be left to the consumer, as it may require knowledge of the producer's internal implementation.
Aggregator pattern helps to solve this problem. It can aggregate data from different services before sending it to consumers. This can be done in two ways:
1.Composite Microservices: All required microservices will be called, data will be merged, and data will be transformed before replying.
2.API Gateway: It is also possible to divide requests into multiple microservices and aggregate data before sending it to consumers.
If you also deal with business logic, it is recommended to choose composite microservices.
c. Client Combination Mode
When developing services by decomposing business capabilities/subdomains, clients must pull data from multiple microservices . In a monolithic world, clients generally only need to call the backend service once to query or refresh all data. However, things will be different now and we need to understand how to do it.
For microservices, the client may need to be designed as a skeleton with multiple sections/areas. Each part calls a separate backend microservice to pull data. This is called Single Page Applications (SPA), and frameworks like AngularJS and ReactJS help make this easy. When a refresh is required, the application can refresh a specific area instead of the entire page.
3. Database schema
a. Database for each service
How to define the database schema for microservices is also a problem :
1.Services must be loosely coupled. They can be independently developed, deployed and scaled.
2.Business request processing may span multiple services.
3.Some transactions require querying data owned by multiple services.
4.Databases sometimes have to be replicated and sharded to scale.
5.Different services have different data storage requirements.
To solve the above problems, each microservice must be designed with a database, which must be dedicated only to that service . It should also only be accessible through the microservice API, not directly by other services. For example, for relational databases, we can use private-tables-per-service, schema-per-service or database-server-per-service. Each microservice should have a separate database id so that it can be given separate access and prevent it from using other service tables.
Private-tables-per-service - each service has a set of tables that can only be accessed by that service
Schema-per-service - each service has a database schema private to that service
Database-server-per-service - each service has its own database
b. Shared database
We've discussed that one database per service is ideal for microservices, but it's possible when the application is completely new and is being developed using DDD. However, if the application is a monolith and trying to evolve into a microservice architecture, it's not so easy. In this case, what is the appropriate architecture?
Shared databases are not ideal, but this is a solution for the above scenario. Most people consider this an anti-pattern for microservices , but for brownfield applications it's a good start to breaking down the application into smaller pieces. In this mode, a database can be called by multiple microservices , but it can only be limited to 2-3 at most , otherwise scalability, autonomy and independence will be difficult to implement.
c. Command Query Duty Separation (CQRS)
Once we have implemented the database for each service, we need to query it. So how do we implement queries in a microservices architecture for federated data from multiple services ?
CQRS recommends splitting the application into two parts - the command side and the query side.
CQRS divides the operations in the system into two categories, namely "Command" and "Query". Command is a general term for operations that will cause data to change, that is, the operations we often say add, update, and delete are all commands. The query is the same as the literal meaning, that is, the operation that does not change the data, but only searches for the data according to certain conditions.
The query side uses a materialized view to process the query part. Views are stored in services that subscribe to events, and each service publishes these events when it updates data. For example, an online store can query customers in a specific region and their recent orders by maintaining a Join view of customer information and order information. The view is updated by services subscribed to the customer information event and the order information event.
d. Saga mode
When each service has its own database and a request transaction spans multiple services, how do we ensure data consistency across services ? For example, for an e-commerce application, the application must ensure that new orders do not exceed the customer's credit limit. Since Orders and Customers are in different databases, applications cannot simply use local ACID transactions.
A Saga represents a high-level business process that consists of multiple sub-requests, each of which updates data in a single service. Each request has a compensation request, which is executed when the request fails.
It can be achieved in two ways:
1.Event Choreography : Without a central coordinator (no single point of risk), each service generates and observes events from other services and decides if action should be taken.
2.Order Orchestrator : The central coordinator is responsible for centralized decision-making and business logic sequencing of events. .
4. Observability pattern
a. Log aggregation
Consider a use case where an application consists of multiple service instances running on multiple machines. Requests typically span multiple service instances, and each service instance generates a log file in a standardized format. How can we understand the application behavior of a specific request through the log?
We need a centralized logging service to aggregate logs from each service instance. Users can search and analyze the logs, and also configure alerts in the logs that trigger when certain messages appear. For example, PCF does have the Loggeregator , which collects logs from every component of the PCF platform (routers, controllers, diego , etc.) and applications. AWS Cloud Watch does the same thing.
b. Performance indicators
As the number of services grows, it becomes critical to keep a close eye on application performance so that you can send alerts when something goes wrong. How should we collect metrics to monitor application performance?
The metrics service is required to collect statistics about individual operations. There are two models of aggregated metrics:
•Push — service to push metrics to metrics services such as NewRelic , AppDynamics, Prometheus
•Pull — The metrics service pulls metrics from services, such as Prometheus
c. Distributed tracing
In a microservices architecture, requests typically span multiple services. So how do we trace a request to troubleshoot problems?
we need a service
•Assign a unique external request ID to each external request.
•Pass the external request ID to all services.
•Include the external request ID in all log messages.
•Centrally record and process information about what happens when an external request is executed.
Spring Cloud Slueth and Zipkin servers are a common class of implementations.
d. Health Check
implementing a microservices architecture, it is possible to encounter issues where services are started but cannot process requests. In this case, how do you ensure that requests are not sent to those failed instances?
Every service needs to have an endpoint that can be used to check the health of the application, e.g. /health , this API should check the status of the host, connections to other services/infrastructure, and any specific logic.
Spring Boot Actuator implements a /health endpoint, and this implementation can also be customized.
5. Cross-disciplinary focus model
a. External configuration
Services often also call other services and databases. Some configuration properties may be different for each environment like dev, QA, UAT, prod. Changes to any of these properties may require rebuilding and redeploying the service. How can we avoid code modification due to configuration changes?
Externalize all configuration, including credentials. The application should load them dynamically at startup.
The Spring Cloud configuration server provides the option to externalize properties to GitHub and load them as environment properties. These can be accessed by the application at startup or refreshed without restarting the server.
b. Service Discovery Mode
When microservices appear, we need to solve several problems in service invocation:
1.Using container technology, IP addresses are dynamically assigned to service instances. Every time the address changes, the consumer service may be interrupted and require manual changes.
2.Each service URL must be remembered by consumers and tightly coupled.
So how does the consumer or router know about all the available service instances and locations?
A service registry needs to be created to hold the metadata for each producer service. Service instances should be registered with the registry on startup and unregistered when stopped. The consumer or router should query the registry and find out where the service is located. The registry also needs to perform health checks on the producer service to ensure that only worker instances of the service are available through it. There are two types of service discovery: client-side and server-side.
An example of service discovery (client side) is Netflix Eureka and an example of service discovery (server side) is AWS ALB.
c. Circuit breaker mode
A service generally calls other services to query data, but downstream services may be down . There are two problems with doing this: First, the request will go all the way to the down service, exhausting network resources and reducing performance. Second, the user experience will be poor and unpredictable. How can we avoid cascading service failures and handle them gracefully?
Consumers should invoke remote services through a proxy, which behaves like a circuit breaker. When the number of consecutive failures exceeds the threshold, the circuit breaker is tripped, and within the timeout period, all attempts to invoke the remote service will fail immediately. After the timeout expires, the circuit breaker allows a limited number of test requests to pass. If these requests are successful, the circuit breaker will resume normal operation. Otherwise, if there is a failure, the timeout period will start over.
Netflix Hystrix is a good implementation of the circuit breaker pattern. It can also use a fallback mechanism when the circuit breaker is tripped. This provides a better user experience.
d. Blue-green deployment mode
With a microservices architecture, an application can have multiple microservices. If we stop all services, and then deploy the core version, the downtime will be very long and affect the business. Also, rolling back would be a nightmare. How can we avoid or reduce service downtime during deployment?
A blue-green deployment strategy can be implemented to reduce or eliminate downtime. It does this by running two identical production environments Blue and Green. Let's assume Green is the existing live instance and Blue is the new version of the application. Only one environment is active at any one time, and the live environment serves all production traffic. Almost all cloud platforms offer the option to implement blue-green deployments.
There are many other patterns used with microservices architecture, such as sidecar, chained microservices, branched microservices, event sourcing pattern, continuous delivery pattern, etc.

Related Articles

Explore More Special Offers

  1. Short Message Service(SMS) & Mail Service

    50,000 email package starts as low as USD 1.99, 120 short messages start at only USD 1.00