How to Migrate Applications to a Container-Managed Microservices Architecture
Introduction: In previous posts, we discussed the microservices trend and some of the complications that may arise from adopting a microservices-based architecture. In this blog, we dive deeper into the challenges and explore the different strategies and design choices available.
Technical aspects of microservices migration strategies overview
Here are three tips for refactoring an application into microservices:
- Packaging: Instead of packaging all the related modules in one package, split the modules into independent packages. This may involve minor changes to the code, or more likely to static content, if you change the application context roots to be separate for each module package.
- Containers: Apply the “container per service” pattern and deploy each package on its own server, preferably in its own container, such as a Docker container. Next, scale the containers independently.
- DevOps: Once the modules are split, you can manage each package independently through an automated DevOps pipeline, such as Delivery Pipeline. This is a step toward gaining the advantages of continuous delivery, i.e., build, deploy and manage independently.
Strategy 1 – Using the Strangler Pattern
In 2004, Martin Fowler, chief scientist at ThoughtWorks, published an article that defined the Strangler Pattern as a way to handle the release of refactored code in a large web application.
Today, the Strangler Pattern is a popular design pattern to incrementally transform a monolithic application into microservices by replacing a particular functionality with a new service. Once the new functionality is ready, the old component is “strangled,” that is, decommissioned, and the new service is put into use. Any new development is done as part of the new service and not part of the monolith.
There are three steps to transition from a monolithic application to microservices by implementing the Strangler Pattern: Transform, Co-Exist and Eliminate.
Figure 1. Three steps to transition from monolithic applications to microservices using the Strangler Pattern
Step 1 – Transform
Create a new parallel site, either in the cloud or the existing environment, but based on modern approaches.
Step 2 – Co-Exist
Leave the existing site where it is for the time being. Redirect from the existing site to the new site, so the functionality is implemented incrementally.
Step 3 – Eliminate
Remove the old functionality from the existing site (or stop maintaining it) as traffic is redirected away from that portion of the old site.
Below are some of the actions required when applying the Strangler Pattern to migrate monolithic applications.
- Identify the size of the application. Strangler Pattern is not suitable for small systems where the complexity is low and the size is small.
- Check the system design and compatibility as this pattern cannot be used in systems where requests to the backend system cannot be intercepted and routed.
- Ensure that the facade does not become a single point of failure or a performance bottleneck.
- Analyze the current legacy monolithic application to split the application based on functional transactions by considering the code complexity. The complexity factor is crucial for the transformation decision using the Strangler Pattern.
- Visualize the legacy components in detail and new modern application in a single view to scope both the code bases.
- Analyze any security threat determinations in the new modern architecture.
- Identify the legacy monolithic application architecture to discover any functional understanding, isolation of components and separation.
- Start developing the modern microservices application per the business functional level for isolated components as a service.
- Include a Strangler facade that can route the functional transactions to legacy or modernized microservices from access channels.
- The legacy application will be larger in size to handle the more functional transaction than the modern microservices application that will handle only the migrated functionality.
- The functional transformation will happen incrementally for identified system functions over time to microservices. The modern application will grow larger and handle more functions than the monolithic legacy application.
- Once all the monolithic functions are transformed into the new microservice application, the transition is complete. All the functions are on the microservices platform, and the monolithic application can now be retired.
Strategy 2 – Using Domain-Driven Design
Domain-driven design is an approach to build software that has complex and ever-changing business requirements. Most organizations are in this situation, as they have complex business processes and are evolving to become even more complex.
Domain-Driven Design (DDD) is a software development approach introduced by Eric Evans in 2003. It requires an understanding of the domain for which the application will be written. The necessary domain knowledge to create the application resides with the people who understand it: the domain experts.
The general migration approach has three steps:
- Stop adding functionality to the monolithic application
- Split the frontend from the backend
- Decompose and decouple the monolith into a series of microservices
Step 1 – Stop Expanding the Monolithic Application
When implementing new functionality, do not add more code to the monolith. Instead, put the new code in a new standalone microservice. This requires installing a request router/API Gateway that handles incoming HTTP requests and routes them to the existing monolith or the newly developed microservice.
Figure 2. Stop Expanding
A service rarely exists in isolation and often needs to access data owned by the monolith. There are three strategies that a service can use to access the monolith’s data:
- Invoke a remote API provided by the monolith
- Access the monolith’s database directly
- Maintain a copy of the data, which is synchronized with the monolith’s database
Step 2: Split the Frontend and Backend (Co-Exist)
This step shrinks the monolithic application and splits the presentation layer between the business logic and data access layers. A typical enterprise application consists of at least three different types of components:
- Presentation layer – Components that handle HTTP requests and implement either a (REST) API or an HTML-based web UI. In an application with a sophisticated user interface, the presentation tier is often a substantial body of code.
- Business logic layer – Components that are the core of the application and implement the business rules
- Data-access layer – Components that access infrastructure components such as databases and message brokers
Figure 3. Split the Frontend and Backend
Step 3 – Extract Services (Eliminate)
The third refactoring step is to turn existing modules within the monolith into standalone microservices. Each time a module is extracted and turned it into a service, the monolithic application shrinks. Once enough modules have been converted, the monolith will either disappear entirely or become small enough to become just another service.
Figure 4. Eliminate the Monolithic Application
Figure 4 shows the before and after refactoring of the application. It is when you break your modules into a separate services.
Migrating enterprise applications to microservices require careful thought and planning. Figure 5 provides a quick maturity assessment, which will help you understand the organization’s maturity and some of the challenges the organization can expect.
Figure 5. The Evolution of the Enterprise Application
Application Maturity Levels
Source: Spring 5.0
Before architecting the transition to microservices, Altran recommends researching the various pathways to get from Point A to Point B, including:
- Choose the microservices architecture that best fits your requirements
- Select the strategy best-suited for the task: either Strangler Pattern or Domain-Driven Design.
- Outline your microservices
- Define the scope of what the microservice should do. What is the breadth of functionality it should implement? If you over partition their functionality, you’ll end up with too many tiny microservices.
- Use RESTful APIs
- When you break a monolithic application into multiple microservices, how will the services talk to one another? Typically it is with the RESTful API.
- Use API Gateways to aggregate data to specific clients
- API Gateways will be responsible for request routing, composition, and protocol translation.
- Build separate teams for specific microservices
- The benefit of separate teams is to make sure you have the necessary skills to build cloud-native applications. You’ll need business analysts, developers, testers and DevOps engineers in each team.
- Setup the server and data storage environment
- Use a separate storage approach to insert a shared, memory-based cache system between a given service and the storage associated with that service.
- Document APIs
- Use tools such as Swagger to design and document your APIs.
- Follow DevOps
- To use the automated build and deployment management of your microservices, you will need a good set of DevOps tools such as Jenkins or TeamCity.
- Built Monitoring
- As an end-user action triggers application work, API calls and service work cascade down the application topology. A single action may result in tens or hundreds of monitorable events.
Connect with a Container-Managed Microservices Architecture expert today.
- Part 1: How to Migrate Applications to a Container-Managed Microservices Architecture
- Part 2: How to Enable Microservices with Container Orchestration tools
- Are you ready for the microservices shift?
List of Additional Sources
- Paradkar, Sameer, “A Full Approach to Migrating to Microservices Architecture,” Nov. 21, 2018, LeanIX
- “What is the Strangler Application pattern?” Feb. 28, 2017, Best Cloud computing
- Behara, Samir, “Monolith to Microservices Using the Strangler Pattern,” Sep. 13, 2018, Microservices Zone
- Brown, Kyle Gene, “Applying The Strangler Fig Application Pattern to Microservices Architectures,” Apr. 6, 2020, IBM Garage, Medium
- Natesan, N. “How to use Strangler Pattern for Microservices Modernization,” Sep. 26, 2019, Software Intelligence Pulse
- Gomez, Maria, “From Monolith to Observable Microservices Using DDD,” ThoughtWorks, InfoQ
- “Monoliths to Microservices using domain-driven design,” Nov. 4, 2019, Microsoft
- Richardson, Chris, “Refactoring a Monolith into Microservices,” Mar. 8, 2016, Eventuate Inc., NGINX
- Osowski, Rick, “How to use development patterns with microservices (part 4),” Jul. 27, 2017, IBM