preface

I’ve been learning about microservices for a long time. Today, I’ve compiled some design patterns commonly used in microservices architecture from the Internet. After reading the whole article in about 8-10 minutes, you should have a deeper understanding of microservices.

The state of the microservices architecture

Microservices architecture has become the de facto choice for modern application development. While it solves some problems, it is not the silver bullet. It has several disadvantages, and many issues must be addressed when using this architecture. This leads to the need to learn common patterns in these problems and use reusable solutions to solve them. Therefore, design patterns for microservices need to be discussed. Before delving into design patterns, we need to understand the building principles of microservices architecture:

  1. scalability
  2. availability
  3. The elastic
  4. Independence, autonomy
  5. Decentralized governance
  6. Fault isolation
  7. Automatic configuration
  8. throughDevOpsContinuous delivery

Applying these principles presents some challenges and specific problems in the actual development delivery process. What we need to focus on is how to solve the problem through the design pattern of microservices.

First, decomposition mode

1. Decomposition by business capacity

The problem

Microservices are about loosely coupling services and applying the single responsibility principle. However, breaking the application into smaller parts must be logical. How do we break down the application into small services?

To solve

One strategy is to break it down by business capability.

Business capability is what an organization does to create value. The set of capabilities for a given business depends on the business type. For example, an insurance company’s capabilities typically include sales, marketing, underwriting, claims processing, billing, compliance, and so on. Each business capability can be viewed as a service, which is a business-oriented rather than technology-oriented decomposition pattern.

2. Decompose by subdomain

The problem

Shredding your application with business capabilities might be a good start, but you’ll encounter so-called God Classes that are often not easy to break down. These classes will be common across multiple services. For example, the Order class can be used for Order management, Order taking, Order delivery, etc. How do we break them down?

To solve

The problem of God Classes can be effectively solved using DDD (Domain-driven design). It uses the concepts of subdomain and bounded context to solve this problem. DDD decomposes the entire domain model created for the enterprise into subdomains. Each subdomain has a model whose scope will be called a bounded context. Each microservice will be developed around a bounded context.

Note: Identifying subdomains is not easy. It requires understanding of the business. As with business capabilities, subdomains are identified by analyzing the business and its organizational structure and identifying different areas of expertise.

The Strangler Model

The problem

So far, the design patterns we’ve talked about have been shredding GreenField Applications, but 80% of the work we’ve done has been in BrownField Applications, This is a large monolithic application. All of the above design patterns do not apply to them, because breaking them down into smaller parts while applying them as a whole is a daunting task.

To solve

The Strangler model can solve such problems. The strangler model uses the analogy of a twining vine. This solution works with Web applications, calling back and forth between Web applications, and for each INVOCATION of a URL, a service can be divided into different domains and hosted as a separate service. The idea is to do one domain at a time, which will create two separate applications that exist in parallel in the same URL space. Eventually, the newly refactored application will “kill” or replace the original application until the entire application can be stopped.

Second, integration mode

1. API gateway mode

The problem

When an application is decomposed into multiple microservices, there are also issues that need to be addressed:

  1. How to invoke multiple microservices to abstract producer information.
  2. On different devices (such as desktops, mobile devices, and tablets), the application needs different data to respond to the same back-end service because the UI may be different.
  3. Different consumers may have different formats for a reusable microservice response, so who does the data conversion or field manipulation.
  4. Producer microservices may not support certain types of protocol processing.

To solve

API gateways help solve many of the problems that arise from microservice implementations, not just the above.

  1. API gatewayIs invoked by the task microserviceSingle entry point.
  2. Acts as a proxy service to route requests to related microservices, abstracting producer details.
  3. A request is sent to multiple services, and the response results are aggregated and sent back to the consumer.
  4. Gm’sAPI gatewayIt can’t meet all consumer needs. It can be created for each specific type of clientFine-grained APIS.
  5. Protocol requests (e.gAMQP) to another protocol (e.gHTTP) and vice versa so that producers and consumers can handle it.
  6. It can also reduce the authentication/authorization responsibilities of microservices.

2. Aggregator mode

The problem

We have discussed how to solve the problem of aggregated data in the API gateway pattern. However, we will discuss it more fully next. For example, when splitting business functions into smaller logical code, it is necessary to consider aggregating the data returned by each service. This task cannot be left to the consumer, who might need to understand the internal implementation of the producer application.

To solve

Aggregation patterns help solve such problems. It discusses how to aggregate data from different services and then send the final response to the consumer. We can do this in the following two ways:

  • Composite microservices: All necessary microservices are invoked, the data is consolidated, and the data is transformed before being rolled back.
  • API gateway: Divide requests into microservices and aggregate data before sending it to consumers.

If any business logic is to be applied, it is recommended to use composite microservices. Otherwise, API gateways are generally recognized as a better solution.

3. Client UI combination mode

The problem

When services are developed by breaking down business functions/subdomains, the service responsible for the user experience must extract data from multiple microservices. In a singleton architecture application, there is only one call from the UI to the back-end service to retrieve all the data and refresh/submit the UI page. However, the microservices architecture is a different story. We need to understand how to do that.

To solve

For microservices, the UI must be designed as a framework with multiple parts/areas of the screen/page. Each part invokes a single back-end microservice to extract data, which is called a UI component that combines specific services. Frameworks like AngularJS and ReactJS do this easily. These screens are called single-page applications (SPAs). This allows the application to refresh specific areas of the screen rather than the entire page.

Third, database mode

1. Each microserver serves a database

The problem

How do you define the database architecture for microservices? Let’s look at some of the issues that need to be addressed:

  1. Services must be loosely coupled. They can be developed, deployed, and extended independently.
  2. Business transactions may enforce invariants that span multiple services.
  3. Some business transactions require querying data from multiple services.
  4. Databases must sometimes be copied and sharded in order to scale.
  5. Different services have different data storage requirements.

To solve

To solve these problems, you must design a database for each microservice. It must be dedicated only to the service, accessible only by the microservice API and not by other services. For example, for relational databases, we can use one dedicated table per service, one schema per service, or one database server per service. Each microservice should have a separate database ID so that separate access can be provided to set up an isolation that prevents it from using tables from other services.

2. Each microservice shares the database

The problem

We have discussed that one database per service is the ideal choice for microservices, which is possible before application development and when developing using DDD. However, if the application is a whole and trying to transform into a microservice, then de-normalization is not so easy. What is the appropriate architecture in this case?

To solve

Sharing a database per service is not ideal, but it is a viable solution to the above situation. Most people think of this as an anti-pattern for microservices, but it’s a good start for BrownField Applications that have been developed to break down Applications into smaller logical parts. This is not suitable for undeveloped GreenField Applications. In this mode, a database can be aligned with more than one microservice, but not unlimited, limited to 2-3 microservices at most, otherwise scalability, autonomy, and independence would be difficult to implement.

3. Command query responsibility isolation (CQRS)

The problem

Once we implement a database for each service, there are bound to be queries, and this requires federated data from multiple services — which is impossible. So how do we implement queries in a microservice architecture?

To solve

Command query Responsibility Isolation (CQRS) recommends splitting your application into two parts — the command side and the query side.

The command side handles create, update, and delete requests.

The query side is responsible for processing the part of the query by using the instantiated view.

The event source pattern is typically used in conjunction with CQRS to create events for any data changes. You can keep the instantiated view up to date by subscribing to the event flow.

Saga mode

The problem

When each service has its own database and a business transaction spans multiple services, how do we ensure data consistency across services? For example, for an e-commerce application where the customer has a credit limit, the application must ensure that new orders do not exceed the customer’s credit limit. Because the order and customer reside in a different database, the application cannot simply use local ACID transactions.

Resolving a Saga is equivalent to a high-level business process consisting of several sub-requests, each updating data in a single service. Each request has a compensation request, which is executed if the request fails. This is done in two ways:

  1. Choreography: When there is no overall coordination, each service generates and listens for another service time and decides if action should be taken.
  2. Orchestration: Responsible for Orchestration of decisions and business logic for a Saga.

Observable mode

1. Log aggregation

The problem

An application consists of multiple service instances running on multiple servers, with requests typically spanning multiple service instances. Log files are generated in a standardized format for each service instance. How can we understand the application behavior of a particular request through logging?

To solve

We need a centralized logging service that aggregates logs for each service instance. Users can search for and analyze logs. They can configure certain messages to trigger alerts when they appear in the log. For example, PCF (Pivotal Cloud Foundy) does have Loggeregator, which collects logs from every component of the PCF platform (router, controller, Diego, etc.) as well as applications. AWS Cloud Watch does the same thing.

2. Performance indicators

The problem

As the service portfolio increases with the use of microservices architectures, it becomes important to monitor transactions so that patterns can be monitored and alarms sent when problems occur. How do we collect metrics to monitor application performance?

To solve

A measurement service is required to collect statistics about individual operations. It should aggregate metrics for application services that provide reporting and alerting. There are two models for aggregation metrics:

  1. Push: service pushes metrics to metrics services, for exampleNewRelic.AppDynamics
  2. Pull (Pull): Metric service extracts metrics from a service, for examplePrometheus

3. Distributed tracking

The problem

In a microservice architecture, requests typically span multiple services. Each service handles requests by performing one or more operations across multiple services. So how do we track end-to-end requests to solve the problem?

Solution We need a service:

  1. Assign a unique external request ID to each external request
  2. Pass the external request ID to all services
  3. Include the external request ID in all log messages
  4. Records information about requests and requests and operations performed when external requests are processed in the centralized service (for example, start time, end time)

Spring Cloud Slueth, Zipkin Server, PinPoint and SkyWalking are the most popular implementations.

4. Health check

The problem

When a microservice architecture is implemented, services may start but cannot process transactions. In this case, how do you ensure that requests don’t go to those failed instances? Use load balancing mode.

To solve

Each service needs to have an endpoint that can be used to check application health, such as: /health. This API should check the state 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.

Five, crosscutting concern mode

1. External configuration

The problem

Services often invoke other services and databases as well. For each environment, such as Dev, QA, UAT, PROD, and so on, the endpoint URL or some configuration properties may be different. Changes to any of these properties may require the service to be rebuilt and redeployed. How do YOU avoid making code changes to configuration changes?

To solve

Externalize all configuration, including endpoint urls and credentials. The application should load them at startup or run time.

Spring Cloud Config Server provides the option to externalize properties to GitHub and load them as environment properties. Applications can access them at startup or refresh them without restarting the server.

Mature industry also ctrip’s Apollo and Alibaba’s Nacos.

2. Service discovery mode

The problem

When microservices appear, we need to resolve some issues about invoking the service:

  1. Using container technology, IP addresses are dynamically assigned to service instances. Each time an address changes, the consumer service may be interrupted and needs to be changed manually.
  2. The URL of each service must be remembered and strongly coupled by the consumer.

So how does the consumer or router know all the service instances and locations available?

To solve

We need to create a service registry that holds metadata for each producer service. Service instances are registered with the registry on startup and unregistered on shutdown. The consumer or router queries the registry and finds out where the service is located. The registry also needs to perform health checks on the producer service to ensure that only work instances of the service are available through it. There are two types of service discovery: client-side and server-side. An example of client-side registry discovery is Netflix Eureka, and an example of server-side registry discovery is AWS ALB.

3. Breaker mode

The problem

Services often invoke other services to retrieve data, and downstream services may fail.

There are two problems with this:

  1. Requests continue to use the service interruption state, depleting network resources and reducing performance.
  2. The user experience will be terrible and unpredictable.

How do you avoid cascading service failures and gracefully handle them?

To solve

A consumer should invoke a remote service through a proxy, which acts like a circuit breaker. When the number of consecutive failures exceeds the threshold, the circuit breaker trips, and all attempts to invoke the remote service fail immediately during the timeout period. After the timeout expires, the breaker allows a limited number of test requests to pass. If these requests are successful, the circuit breaker will resume normal operation. Otherwise, if a failure occurs, the timeout cycle starts again.

The Netflix Hystrix is a good implementation of the circuit breaker mode. It also helps you define an alternate mechanism that can use circuit breakers to trip. This provides a better user experience.

4. Blue-green deployment mode

The problem

With the microservices architecture, an application can have many microservices. If we were to stop all services and then deploy an enhanced version, the downtime would be significant and could affect the business. Moreover, a rollback would be a nightmare. How can we avoid or reduce service downtime during deployment?

To solve

Blue-green deployment strategies can be used to reduce or eliminate downtime. It does this by running two identical production environments (blue and green). Let’s assume that green is the existing activity instance and blue is the new version of the application. Only one environment is running at any one time, and that environment serves all production traffic. All cloud platforms offer the option to implement blue-green deployments.

conclusion

Many other patterns are used with the microservice architecture, such as Sidecar, chained microservice, branch microservice, event source pattern, continuous delivery pattern, and so on. The list continues to grow as we gain more experience with microservices.

Recommended reading

⭐ “2021 Year-end Summary” 10 years deep Floating, 3 cars, 3 apartments ⭐

⭐ “Super Architect” DevOps Beginner’s Guide ⭐

⭐ “Super Architect” 1.5W tutorial on distributed systems ⭐

⭐ “Super Architect” ten maps to thoroughly understand the evolution of web architecture ⭐

⭐ “Super Architect” is probably the best Redis distributed lock implementation ⭐