Microservice Practice (7): Migrating from Monolithic Architecture to Microservice Architecture

This is the seventh and final post in a series on developing applications with microservices. The first article introduces the microservice architecture pattern and discusses the advantages and disadvantages of the microservice architecture; the subsequent articles discuss different aspects of the microservice architecture: using API gateways, inter-process communication, service discovery, event-driven data management, and deploying microservices. In this article, we'll explore strategies to consider when migrating an application from a monolithic to a microservices architecture.

 

I hope readers have a good understanding of the advantages and disadvantages of microservices through this series of articles, and when to use this architecture. Maybe a microservices architecture is more suitable for your application. Maybe you're developing a large, complex monolithic application where the day-to-day development and deployment experience is very slow and painful, and microservices seem like a bliss from afar. Fortunately, there are strategies out there that you can refer to, and in this article, I will describe how to gradually migrate a monolithic application to a microservices architecture.

 

The other six articles in this series are listed below:

  • Microservices in Practice (1): Advantages and Disadvantages of Microservice Architecture
  • Microservices in Practice (2): Using API Gateway
  • Microservice combat (3): In-depth inter-process communication of microservice architecture
  • Microservices in Practice (4): Feasible Solutions and Practical Cases for Service Discovery
  • Microservice Practice (5): Event-Driven Data Management of Microservices
  • Microservice Practice (6): Choosing a Microservice Deployment Strategy

Migrating to Microservices Overview

 

Migrating a monolithic application to a microservices architecture means a series of modernization processes, kind of like what these generations of developers have been doing, and in real time, we can reuse some ideas when migrating.

 

One strategy: don't do a big bang code rewrite (only if you're committed to rebuilding a whole new set of microservices-based applications). Rewriting code sounds great, but it's actually fraught with risk and can eventually fail, as Martin Fowler said: "the only thing a Big Bang rewrite guarantees is a Big Bang!"

 

Instead, a strategy of gradually migrating monolithic applications should be adopted. By gradually generating new applications of microservices and integrating with old monolithic applications, over time, the proportion of monolithic applications in the entire architecture will gradually decrease until they disappear or become Part of the microservice architecture. This strategy is a bit like doing maintenance on a car at a speed limit of 70 mph on a highway, although it is challenging, but much less risky than a rewrite.

 

Martin Fowler called this modern strategy the Strangler application, named after the strangler vine in the rainforest, also known as the strangler fig. In order to climb to the top of the forest, the strangling vine had to grow around the uncle. After a period of time, the tree died, leaving a tree-shaped vine. This kind of application also uses the same model, and develops new microservice applications around traditional applications, which will gradually withdraw from the stage.

 

 

Let's look at other possible strategies.

 

Strategy 1 - Stop mining

 

The Law of Holes says that when you enter a hole, you should stop digging. This is the best advice when a monolithic application is unmanageable. In other words, you should stop letting your monolith continue to get bigger, which means you shouldn't add new code to the old monolith when new features are developed, and the best approach should be to develop new features as independent microservices. As shown below:

 

In addition to new services and traditional applications, there are two modules, one of which is the request router, which handles incoming (http) requests, a bit like the API gateway mentioned earlier. Routers send new feature requests to newly developed services, while traditional requests are also sent to monolithic applications.

 

The other is glue code, which integrates microservices and monolithic applications. Microservices rarely exist independently, and often access data from monolithic applications. Glue code, which may be in a monolithic application or as a service or both, is responsible for data integration. Microservices use glue code to read and write data from the monolithic application.

 

There are three ways for microservices to access monolithic application data:

 

Remote API provided by the ventilation unit application

 

Direct access to the monolithic application database

 

Maintain a copy of your own data synced from the monolith

 

The glue code is also known as the anti-corruption layer, because the glue code protects the new domain model of microservices from the contamination of the traditional monolithic application domain model. The glue code provides translation functionality between the two models. The term anti-corruption layer first appeared in the must-read Domain Driven Design by Eric Evans and was subsequently distilled into a white paper. Developing a DR layer may be a little trivial, but it is a necessary part of avoiding the monolithic quagmire.

 

Implementing new functionality as lightweight microservices has many advantages, such as preventing monoliths from becoming more unmanageable. Microservices themselves can be developed, deployed, and scaled independently. Adopting a microservice architecture will bring different personal feelings to developers.

 

However, this approach doesn't solve any of the monolith's own problems, and in order to solve the monolith's own problems, changes must be made deep into the monolith. Let's look at a strategy for doing so.

 

Strategy 2 - Separating Frontend and Backend

 

The strategy to reduce the complexity of a monolithic application is to separate the presentation layer from the business logic and data access layers. A typical enterprise application consists of at least three distinct elements:

 

Presentation Layer - Handles HTTP requests, either responding to a REST API request or providing an HTML-based graphical interface. For a complex user interface application, the presentation layer is often an important part of the code.

 

Business logic layer - the application core that completes business logic.

 

Data Access Layer - Access to foundational elements such as databases and message brokers.

 

There is a clear separation between the presentation layer and the business data access layer. The business layer has a coarse-grained API composed of several aspects, which contains business logic elements. API is a natural boundary that can divide a single business into two smaller applications, one of which is the presentation layer, and the other is the business and data access logic. After the split, the presentation logic application calls the business logic application remotely. The following figure shows the different architectures before and after the migration:

 

 

There are two advantages of splitting a monolithic application in this way. First, it makes the development, deployment and expansion of the two parts of the application independent. In particular, it allows the developers of the presentation layer to quickly select and conduct A/B tests on the user interface; Remote APIs can be called by microservices.

 

However, this strategy is only a partial solution. It is very likely that one or both of the two parts of the application are unmanageable, so a third strategy is needed to eliminate the remaining monolithic architecture.

 

Strategy 3 - Extracting Services

 

The third migration strategy is to extract certain modules from the monolithic application into independent microservices. Whenever a module is extracted and turned into a microservice, the monolithic application becomes simpler; once enough modules are converted, the monolithic application itself is no longer a problem, either disappears or simply becomes a service.

 

Sort which module should be turned into a microservice

 

A huge complex monolithic application consists of dozens or hundreds of modules, each of which is an extracted object. Determining the first module to be extracted is generally a challenge, and it is generally best to start with the easiest module to extract, which will allow developers to accumulate enough experience, which can bring great benefits to subsequent modularization work.

 

Converting modules into microservices is generally time-consuming, and can generally be sorted according to the degree of benefit. Generally, starting with frequently changing modules will benefit the most. Once a module is converted into a microservice, it can be developed and deployed as a standalone module, speeding up the development process.

 

It is also one of the sorting criteria to extract large resource consumers first. For example, it can be useful to abstract an in-memory database into a microservice that can be deployed on a large-memory host. Likewise, it can be beneficial to abstract algorithmic applications that are sensitive to computing resources, and that such services can be deployed on hosts with many CPUs. By converting resource consumption modules into microservices, applications can be easily extended.

 

It is also beneficial to look up existing coarse-grained boundaries to decide which modules should be extracted, making porting easier and simpler. For example, a module that only synchronizes messages asynchronously with other applications is a clear boundary that can be easily turned into a microservice.

 

How to extract modules

 

The first step in extracting a module is to define a coarse-grained interface between the module and the monolithic application. Since the monolithic application requires data from microservices and vice versa, it is more like a two-way API. Developing such APIs is challenging, especially for the business logic layer that uses the Domain Model pattern, because it has to balance responsibility for dependencies with fine-grained interface patterns, so code changes are often required to address Dependency issues, as shown:

 

Once the coarse-grained interface is complete, this module is transformed into a standalone microservice. To do this, code must be written so that information is exchanged between the monolith and the microservices through an API using an inter-process communication (IPC) mechanism. The comparison before and after migration is shown in the figure:

 

 

In this example, the Z module that is using the Y module is an alternative extraction module, and its elements are being used by the X module. The first step in the migration is to define a set of coarse-grained APIs. The first interface should be the internal interface used by the X module. , used to activate the Z module; the second interface is an external interface used by the Z module to activate the Y module.

 

The second step in the migration is to convert the module into a standalone service. Both internal and external interfaces use code based on the IPC mechanism, and the Z module is generally integrated into a microservice basic framework to solve problems in the cutover process, such as service discovery.

 

After the module is extracted, another service can be developed, deployed, and extended, independent of the monolithic application and other services. The service can be implemented by writing code from scratch; in this case, the API code that integrates the service and the monolithic application becomes the disaster recovery layer, which performs translation work between the two domain models. Every time a service is extracted, a step is taken in the direction of microservices. Over time, monolithic applications will become simpler, and users can add more independent microservices.

 

Summarize

 

Migrating existing applications to modern applications with a microservice architecture should not be done by rewriting code from scratch, but by gradual migration. There are three strategies to consider: implement new functions as microservices; separate the presentation layer from the business data access layer; extract existing modules into microservices. Over time, the number of microservices will increase, and the resiliency and efficiency of the development team will greatly increase.

 

This article is reprinted from: http://www.jianshu.com/p/c63a739914bf

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326375746&siteId=291194637