Organizations and companies often have large monolithic applications that serve and support business requirements. Those applications are rigid and hard to deal with when it comes to maintenance and fulfilling changes required to shift or improve businesses quickly. A change in a monolithic application often requires the code-base to be rebuilt and recompiled, which may affect other services and components during the implementation and deployment of the changes; due to the high intercomponent dependency. The impact is proportionate to the size of the application. In addition, it often requires down-time to deploy a new version of the application, which may have impact on Time to Market.
It is vital to measure the risks and understand the complexity in development and operation that result from transforming a monolithic application to microservices. Microservices requires organizational changes, such as breaking the development team into small teams per microservice and special skills in developing and operating microservices. Once all aspects are considered and the value of the transformation is realized, this blog can be used as a guide for the transformation.
A Journey to Microservices
Before one starts breaking down a monolithic application, the architect or developer has to analyze the application. The first thing to look at is the application functions. Application functions represents business and technical requirements. The functions can be grouped into domains. The example for this article would be a checkout application for an e-commerce website. The user searches for items and maybe compares prices, then the chosen items are added to a cart. At the end of the shopping process, the user checks out the items. At this stage, the list of about to be purchased items, the unit price of each item, total price, and VAT values are presented and calculated. Figure 1 shows the application as monolithic that performs the checkout. Note here that the application acts as a black box that performs and provides all the required data.
After realizing the monolithic application, the next step is to divide the application into domains. Design-Driven Design is a software engineering approach that aims to draw the context boundaries of application components. In the Checkout example, the domains could be supplier, carrier, and payment. The supplier domain covers, for example, the stock availability and the characteristics of the items. The carrier domain includes the shipment rates, for example. Finally, the payment domain could cover the total price and VAT calculation.
Domains helps define the context boundaries. Context boundaries focuses on creating the business representation of isolated islands. Using the context boundaries, the scope of each team could be defined. This means the team of the monolithic application (business, operation, development, etc.) can be divided based on those contexts.
The next phase is create decoupled and independent components for each defined domain. Each component serves a single business requirement. This fulfills the Single Responsibility Principle, where each component has only one reason to change. In another word, each component represents the most basic and simple business requirement. The main goal of this activity is to break up the code-base into decoupled components.
At this stage, the logical microservice representation of the monolithic application is becoming clearer. The question now would be, where to start to refactoring? In general there are two types of application refactoring: big bang and phase-based refactoring. The big bang approach requires developing the microservices independent and in isolation of the existing legacy system. The code is not tested in production before deploying the full fabric. The phase-based approach, in contrary, offers to revamp a small, independent part of the application; going through the phases until the application is replaced with microservices. It is agile-based and it offers the benefits of that approach. In this article, the phase-based approach will be applied to the Checkout application.
The decoupled components have dependencies on other components and services. This dependency must be taken into consideration during the transformation. Hence, It is recommended to start with the edge services. Edge services are services that require minimum logic, basic routing, and the least dependency among other services. An example of an edge service would be payment authorization. Payment authorization is usually performed by 3rd party; i.e. a bank or a credit company. In addition, payment authorization as a model can be reused by other systems in the environment, for example subscription. It is an independent service that can be extracted from the application without affecting the business logic of the code. Hence, it minimizes the risk of business impact.
Once the edge service is realized, the interface of this service must be built. The interface defines the means of communication and interaction of the service. The following diagram illustrates the interaction after extracting the edge service from the application.
In summary, a monolithic application needs to be analyzed from domain perspective. This helps in identifying the target areas for the application revamp. Using the Domain-Driven Design (DDD) to create the context boundaries of the application helps in achieving the full realization of the application’s domains. Functions, within a domain, can be broke into decoupled components. It is important to analyze the dependency between the components. Hence, it is recommended to start with edge services when a monolithic application needs to be transformed into microservices.