Devops and Microservices -Unit-2
Devops and Microservices -Unit-2
Unit-II
Monolithic architecture
Monolithic Architecture is like a big container, wherein all the software components of an app are
assembled and tightly coupled, i.e., each component fully depends on each other.
Monolithic applications are single-tiered, which means multiple components are combined into one
large application. Consequently, they tend to have large codebases, which can be cumbersome to
manage over time.
Furthermore, if one program component must be updated, other elements may also require rewriting,
and the whole application has to be recompiled and tested. The process can be time-consuming and
may limit the agility and speed of software development teams. Despite these issues, the approach is
still in use because it does offer some advantages. Also, many early applications were developed as
monolithic software, so the approach cannot be completely disregarded when those applications are
still in use and require updates.
Monolithic architecture in software often requires a whole application to be recompiled even if only
one part is changed.
Example:
Let’s take an example of an e-commerce site-
To understand monolithic architecture, let's take an example of a banking application. The banking
application website first authorizes customers, logs them in to their account and enables them to make
online money transfers to other accounts. There are several components involved in this entire process,
including the customer-facing user interface, plus services for user authentication, statement
downloads, money transfers, etc.
If the application uses a monolithic architecture, it is built and deployed as a single application,
regardless of how a customer uses it. Thus, whether users access the application from their desktop or
from a mobile device, the application remains tightly coupled, and all the various components
and modules are directly connected to each other. It may also use a relational database management
system as a single data source. Finally, if changes are needed for any one component, code changes
are required for all other affected components as well.
Ease of implementation — You might think that monolithic systems would be easier to implement,
since the software comes from a single vendor. That’s not always the case. Because monolithic
systems tend to be complex, they may be as difficult to roll out as multiple individual platforms. One
area where they can have an advantage is that monolithic systems are a one-stop shop for support —
but that’s only an advantage if the vendor has a reputation for good support.
Vendor lock-in — Typically, monolithic systems attempt to cover a broad set of related functions. A
monolithic web hosting platform, for instance, might include not just a web server that handles HTTP
requests on the server side but also firewalls, load balancing, and a content distribution network. But
because they’re designed to “do it all,” monolithic systems typically don’t work well with other
systems. Which ties into the next point.
Control and ownership of their data — Monolithic systems don’t make it easy for organizations to
integrate the data from their systems. You typically can use your data only within the monolith. For
example, a monolithic analytics system that includes data integration, ETL data pipelines, a data
warehouse, and analytics software may not provide tools that permit organizations to access their own
data to integrate it with other systems, or run analytics using different software.
Return on investment (ROI) — There’s no point in rolling out any application without a positive
ROI. Whether you’re developing your own applications or deploying SaaS(software as a service)
solutions, your software engineering team can create microservices relatively quickly, roll them out
as they’re ready, and let customers (external or internal, depending on the application) begin using
them. You can get a faster time to market and a positive ROI from your services incrementally as you
deploy them.
Key components of monolithic applications
Monolithic applications typically consist of multiple components that are interconnected to form one
large application. These components may include these features:
Some applications may also include a notification module to control and send automated email
communications to users.
The single codebase of monolithic software architecture tasks such as configuration management and
other development concerns
Drawbacks of monolithic architecture
Drawbacks:
Maintenance — If Application is too large and complex to understand entirely, it is challenging to
make changes fast and correctly.
The size of the application can slow down the start-up time.
Monolithic applications can also be challenging to scale when different modules have conflicting
resource requirements.
Reliability — Bug in any module (e.g. memory leak) can potentially bring down the entire process.
Moreover, since all instances of the application are identical, that bug impact the availability of
the entire application
Regardless of how easy the initial stages may seem, Monolithic applications have difficulty to
adopting new and advance technologies. Since changes in languages or frameworks affect an entire
application, it requires efforts to thoroughly work with the app details, hence it is costly considering
both time and efforts.
Today, many organizations are moving away from monolith architectures and adopting a micro
services architecture (MSA) because it offers multiple advantages.
Disadvantage:
1. Large and Complex Applications:
For large and complex application in monolithic, it is difficult for maintenance because they are
dependent on each other.
2. Slow Development:
3. It is because, for modify an application we have to redeploy whole application instead of updates
part. It takes more time or slow development.
4. Unscalable:
Each copy of the application will access the hole data which make more memory consumption.
We cannot scale each component independently.
5. Unreliable:
If one services goes down, then it affects all the services provided by the application. It is because
all services of applications are connected to each other.
6. Inflexible:
Really difficult to adopt new technology. It is because we have to change hole application
technology.
Advantages of microservices architecture
MSA supports modular applications where any single module in a system, such as a microservice, can
be changed independently without affecting the other parts of the program and without creating
unanticipated changes within other elements.
Modular programs are also more adaptable to iterative development processes and Agile practices
compared to monolithic programs. They are also more scalable and can be tested individually due to
loose coupling between the various components. Modules also communicate with each other, have
their own databases and increase application startup speed.
Microservices Architecture
Microservices are an approach to application development in which a large application is built as a
suite of modular services (i.e. loosely coupled modules/components). Each module supports a specific
business goal and uses a simple, well-defined interface to communicate with other sets of services.
Instead of sharing a single database as in Monolithic application, each microservice has its own
database. Having a database per service is essential if you want to benefit from microservices, because
it ensures loose coupling. Each of the services has its own database. Moreover, a service can use a type
of database that is best suited to its needs.
Consider the same example of the e-commerce application, which consists of several
components/modules. Define each component/module as a separate loosely coupled service depending
on the requirement, which may collaborate with each other based on the scenario. We can have
following services for a complete application:
Cart Service — Manage user cart, this service can utilize Catalog service as a data source.
Benefits:
Microservices Enables the continuous delivery and deployment of large, complex applications.
It enables you to organize the development effort around multiple teams. Each team is responsible
for one or more single service. Each team can develop, deploy and scale their services
independently of all of the other teams.
The application starts faster, which makes developers more productive, and speeds up deployments
Improved fault isolation. For example, if there is a memory leak in one service then only that
service is affected. The other services continue to handle requests. In comparison, one misbehaving
component of a monolithic architecture can bring down the entire system.
Drawbacks:
Developers must deal with the additional complexity of creating a distributed system.
Developer tools/IDEs are oriented on building monolithic applications and do not provide explicit
support for developing distributed applications.
Implementing use cases that span multiple services without using distributed transactions is
difficult.
Implementing use cases that span multiple services requires careful coordination between the
teams.
Deployment complexity. In production, there is also the operational complexity of deploying and
managing a system comprised of many different service types.
Microservices are a type of application architecture that builds cloud applications. Learn more about
microservices, how micro service architecture works, its benefits, and applications.
In recent years, the rise of the internet and the ubiquity of mobile computing have made it necessary
for application developers to design their applications focusing on a lightweight, self-contained component.
Developers need to deploy applications quickly and make changes to the application without a
complete redeployment. This has led to a new development paradigm called “micro services,” where
an application is broken into a suite of small, independent units that perform their respective functions
and communicate via APIs.
Although independent units, the application may pull any of these microservices to work together and
achieve the desired results.
Developers have long waited for an alternative to monoliths to achieve the scalability, simplicity and
flexibility needed to create highly sophisticated software applications.
Monolithic architecture is like a large container holding all software components of an application:
user interface, business layer, and data interface. This had several limitations, including inflexibility,
lack of reliability, difficulty scaling, slow development, etc. It was to bypass these issues that micro
services architecture was created.
Applications running on micro services architecture are deconstructed and separated into smaller
units (microservices) that the client can access using an API gateway. These microservices are each
independently deployable but can communicate with one another when necessary.
Breaking down an application into microservices allows for faster development, easier bug detection
and resolution, smoother maintenance, flexibility, and higher availability and scalability.
Micro services architecture focuses on classifying the otherwise large, bulky applications. Each micro
service addresses an application’s particular aspect and function, such as logging, data search, and
more. Multiple such micro services come together to form one efficient application.
This intuitive, functional division of an application offers several benefits. The client can use the user
interface to generate requests. At the same time, one or more micro services are commissioned through
the API gateway to perform the requested task. As a result, even larger complex problems that require
a combination of micro services can be solved relatively easily.
The other way around, Microservices are independently releasable services modeled around a business
domain. A service encapsulates functionality and makes it accessible to other services via networks—
you construct a more complex system from these building blocks.
One microservice might represent an inventory, another order management, and yet another shipping,
but together they might constitute an entire eCommerce system. Microservices are an architecture
choice that gives you many options for solving the problems you might face.
About 87% of those who have adapted to the microservices architecture believe that it could soon
become the favorite application of executives and project managers as it is now of developers. They
are in tune with how the corporate owners intend to build, run and maintain their teams.
Due to the independent nature of microservices, smaller development teams can parallelly work on
different components to update existing functionalities. This makes it significantly easier to identify
hot services, scale independently from the rest of the application, and improve the application.
Each microservice constituting an application needs to be a full stack. This enables microservices to
be deployed independently at any point. Since the microservices are granular in nature, it’s easy for
development teams to work on one microservice to fix errors and then redeploy it without redeploying
the entire application.
Microservice architecture is agile and thus does not need a congressional act to modify the program
by adding or changing a line of code or adding or eliminating features. The software offers to
streamline business structures through resilience improvisation and fault separation.
In monolithic applications, the failure of even a small component of the overall application can make
it inaccessible. In some cases, determining the error could also be tedious. With microservices,
isolating the problem-causing component is easy since the entire application is divided into standalone,
fully functional software units. If errors occur, other non-related units will still continue to function.
A technology stack refers to a set of programming languages, databases, front-end and back-end tools,
frameworks, and other components the developers use to build an application. With microservices,
developers have the freedom to pick a technology stack that is best suited for one particular
microservice and its functions. They have absolute control instead of opting for one standardized tech
stack that encompasses all of an application’s functions.
Put simply; the microservices architecture makes app development quicker and more efficient. Agile
deployment capabilities combined with the flexible application of different technologies drastically
reduce the duration of the development cycle. The following are some of the most vital applications
of microservices architecture.
1. Data processing
Microservices speed up data processing tasks since applications running on microservice architecture
can handle more simultaneous requests. Larger amounts of information can be processed in less time,
allowing faster and more efficient application performance.
2. Media content
Companies like Netflix and Amazon Prime Video handle billions of API requests daily. Services such
as OTT platforms offering users massive media content will benefit from deploying a microservices
architecture. Microservices will ensure that the plethora of requests for different subdomains
worldwide is processed without delays or errors.
3. Website migration
Website migration involves a substantial change and redevelopment of a website’s major areas, such
as its domain, structure, user interface, etc. Using microservices will help you avoid business-
damaging downtime and ensure your migration plans execute smoothly without any hassles.
4. Transactions and invoices
Microservices are perfect for applications handling high payments and transaction volumes and
generating invoices for the same. The failure of an application to process payments can cause huge
losses for companies. With the help of microservices, the transaction functionality can be made more
robust without changing the rest of the application.
Microservices tools
Building a microservices architecture requires a mix of tools and processes to perform the core
building tasks and support the overall framework. Some of these tools are listed below.
1. Operating system
Arguably, the most basic tool required to build an application is an operating system (OS). One such
operating system allows great flexibility in development and uses in Linux. It offers a largely self-
contained environment for executing program codes and a series of options for large and small
applications in terms of security, storage, and networking.
2. Programming languages
One of the benefits of using a microservices architecture is that you can use a variety of programming
languages across applications for different services. Different programming languages have different
utilities deployed based on the nature of the microservice.
3. API management and testing tools
The various services need to communicate when building an application using a microservices
architecture. This is accomplished using application programming interfaces (APIs). For APIs to work
optimally and desirably, they need to be constantly monitored, managed and tested, and API
management and testing tools are essential for this.
4. Messaging tools
Messaging tools enable microservices to communicate both internally and externally. Rabbit MQ and
Apache Kafka are examples of messaging tools deployed as part of a microservice system.
5. Toolkits
Toolkits in a microservices architecture are tools used to build and develop applications. Different
toolkits are available to developers, and these kits fulfill different purposes. Fabric8 and Seneca are
some examples of microservices toolkits.
6. Architectural frameworks
Microservices architectural frameworks offer convenient solutions for application development and
usually contain a library of code and tools to help configure and deploy an application.
7. Orchestration tools
A container is a set of executables, codes, libraries, and files necessary to run a microservice. Container
orchestration tools provide a framework to manage and optimize containers within microservices
architecture systems.
8. Monitoring tools
Once a microservices application is up and running, you must constantly monitor it to ensure
everything is working smoothly and as intended. Monitoring tools help developers stay on top of the
application’s work and avoid potential bugs or glitches.
9. Serverless tools
Serverless tools further add flexibility and mobility to the various microservices within an application
by eliminating server dependency. This helps in the easier rationalization and division of application
tasks.
The microservices architecture comes with its fair share of challenges, from deployment to operation
and maintenance. Some of these challenges are discussed below.
1. Inter-service communication
Although microservices can exist and function independently, they often need to interact and
communicate with other microservices to fulfill certain demands or tasks. This necessitates
maintaining a fully functional API serving as a communication channel between multiple services
constituting the application.
2. Distributed logging
When different and independently operating microservices are deployed as part of an application, each
of these services has a distinct logging mechanism. This results in large volumes of distributed log
data that are unstructured and difficult to organize and maintain.
3. Transaction spanning
Distributed transactions refer to transactions that require the deployment and proper functioning of a
series of microservices to run. This means that a transaction spans multiple microservices and
databases and a small failure in just one will result in a transaction failure.
With recent advances in cloud technology, many big brands have now advocated moving from a
monolithic to a microservices architecture for better functionality. Let’s look at two such companies
that have vastly improved their business by leveraging microservices.
1. Amazon
If you look at Amazon’s retail website in 2001, it essentially worked as a single, monolithic
application. The lack of application flexibility made developers struggle while untangling
dependencies when upgrading or scaling the services. As a result, Amazon struggled to meet the needs
of its rapidly growing customer base.
To solve this issue, the Amazon development team split the large, monolithic application into smaller,
independent services managed and handled by separate developer teams. Amazon’s efforts eventually
led to the creation of a highly decoupled, service-oriented architecture that we now refer to as
microservices architecture. These changes made Amazon overcome all of its scalability and service
outage problems and hit a market cap of $1.7 million. The image below is a visual representation of
the microservices architecture deployed by Amazon in 2008.
Image source
2. Netflix
Netflix started its movie streaming business in 2007. Within just a year, it started facing severe
scalability issues and service outages. In 2008, Netflix failed to ship DVDs to customers for three
consecutive days. This is when they decided to switch to a distributed cloud system with Amazon Web
Services (AWS) as the cloud provider.
Later in 2009, Netflix began moving its application architecture from monolithic to microservices, and
this process was finally completed in 2012.
This move to a microservices architecture enabled Netflix to overcome its scalability challenges and
offer its services to millions around the world. In 2015, Netflix’s API gateway successfully processed
2 billion API requests daily – thanks to a group of over 500 cloud-hosted microservices. The cost of
streaming was also reduced, which allowed Netflix to make significant financial gains. Netflix’s
application consisted of over 700 microservices by 2017, and the streaming service has grown
exponentially over the past decade.
Microservices architecture is the most accepted and reliable approach to developing cloud applications
today. It’s highly backed due to the various advantages it brings to tech development teams in the form
of shorter development times, improved flexibility, and greater reliability.
It is also unnecessary for a business with existing applications to build the microservices architecture
from scratch. Several options are now available that allow converting web applications from
monolithic to microservices.
Scalability is one of the primary motivations for moving to a microservice architecture. Moreover, the
scalability effect on rarely used components is negligible. Components that are used by the most
users should therefore be considered for migration first.
Users want their interactions with a system to return the right data at the right level of detail, usually
as fast as that data can be acquired. The jobs for users each involve one or more data objects, and each
data object has a set of associated actions that can be performed. The development team that designs
and implements the system must consider the collection of jobs, data objects, and data actions. A
typical process to migrate from a monolithic system to a microservices-based system involves the
following steps:
o Identify logical components.
o Flatten and refactor components.
o Identify component dependencies.
o Identify component groups.
o Create an API for remote user interface.
o Migrate component groups to macroservices (move component groups to separate projects and
make separate deployments).
Migrate macroservices to microservices.
Repeat steps 6-7 until complete.
I describe "components," the "remote user interface," and "macroservices" in more detail below.
In this process, developers perform steps 1-5 once at the beginning of the migration project. The team
can revisit these steps as processes get migrated or when new information becomes available during
the migration process, but once the steps are completed, it should not be necessary to revisit them.
Step 6 is an interim step that may not be needed for some projects. If a macroservice is not needed,
developers can skip step 6; step 7 then becomes, "Migrate component groups to microservices."
Likewise, developers can skip step 7 or a particular iteration if the migration from macroservice to
microservice appears too hard or if the macroservice transition is adequate by itself.
Step 8 exists because developers need not perform the entire migration at once. They can pull
components out of the legacy monolith and make them into microservices, either singly or in groups
as the development warrants.
Components and Component Groups
The components are the logical sets of data objects and the actions that the system performs on
those objects. The actions performed on data objects are typically encoded in software functions and
methods, and these functions and methods are typically stored in software libraries. The component
groups roughly correspond to software libraries, but the migration process may rearrange the contents
of existing software libraries to meet the migration goals.
Macroservices
A macroservice is similar to a microservice with two primary differences. First, a macroservice may
share the datastore with the legacy monolithic system or other macroservices. Second, unlike a
microservice, a macroservice may provide access to multiple data objects and jobs to be done.
Because of these differences from microservices, macroservices may suffer the same problems as
monolithic systems regarding scalability and maintainability. However, because they are typically
focused on fewer data objects and interactions than the original monolithic systems, they can be a
useful interim step during the migration process. It is possible to realize some benefits from the
reduced complexity while the architects determine the best way to untangle the remaining
interconnections.
API Proxy/Façade
The application programming interface (API) proxy is the mechanism through which all dataflows to
the underlying services must traverse. This API is used by both user interfaces and backend systems.
During the migration, the monolithic system must be modified so that the components that have been
migrated (either to macroservices or microservices) use the API to access the migrated data. The
monolithic system must also be modified so that the API proxy can communicate with the legacy
system to perform the actions that have not yet been migrated. The API proxy can then be used to
access the data whether it is accessible through the monolithic service, a microservice, or an interim
macroservice.
Only a single migrated microservice may be allowed to access the data directly. All other users of that
data must use the API for access. When the migration is complete, the API remains the only means to
access the data.
Ideally, a macroservice would have the same exclusive access to its datastore for all relevant
information, but sometimes it might need to access the datastore of the legacy monolithic application
or another macroservice. However, if the macroservice does include a datastore that is separate from
the legacy monolithic application, that monolith should not be able to access the macroservice's
datastore directly; the monolith should always use the API for that data.
This API proxy is sometimes referred to as the façade because it masquerades as the actual
microservices that are behind it. After the underlying services have been migrated and can respond to
API calls directly, the API proxy may be removed.
The 8-Step Migration Process
1. Identify Logical Components
There are three main information components with the data used in the system:
o data objects
o data actions
o job to perform and use cases
The data objects are the logical constructs representing the data being used. The data actions are the
commands that are used on one or more data objects, possibly on different types of data, to perform a
task. The job to perform represents the function the users are calling to fulfill their organizational roles.
The jobs to perform may be captured as use cases, user stories, or other documentation involving user
input.
Figure 1: Identify Logical Components
When combining multiple systems into a unified system, the data objects, data actions, and jobs to
perform for each individual system must be identified. All these components are implemented as
modules within the codebase with one or more modules representing each data object, data action, and
job to perform. These modules should be grouped into categories for working with later steps. This
grouping is indicated by color coding in Figure 1.
System architects may find it easiest to identify the data objects used within a system. Working from
this dataset, they can then determine the data actions and map these to the jobs to performed by users
of the system. The codebase is usually object-centric, and each code object is associated with functions
and jobs to perform.
During this part of the migration process, system architects should be asking the following questions:
o If two or more applications provide similar data, can this data be merged?
o What should be done about data fields being different or missing in similar objects?
The migration from a monolithic system to microservices does not typically affect the user interface
directly. The components that are best for migrating are thus determined by which components
o are used by the most users
o are used most frequently
o have the fewest dependencies on other components
o are performing too slowly
2. Flatten(compress) and Refactor Components
After all the modules have been uniquely identified and grouped, it is time to organize the groups
internally. Components that duplicate functionality must be addressed before implementing the
microservice. In the final system, there should be only one microservice that performs any specific
function. Function duplication will most likely be encountered when there are multiple monolithic
applications being merged. It may also arise where there is legacy (possibly dead) code that is included
in a single application.
Merging duplicated functions and data will require the same considerations as when designing the
ingestion of a new dataset:
o Check data formats.
o Verify datatypes.
o Verify data accuracy.
o Verify data units.
o Identify outliers.
o Deal with missing fields or values.
Since one of the effects of this migration is to have a single data repository for any piece of data, any
data that is replicated in multiple locations must be examined here, and the final representation
must be determined. The same data may be represented differently depending on the job to be done.
It is also possible that similar data may be obtained from multiple locations, or that the data may be a
combination from multiple data sources. Whatever the source and however the data will be used, it is
essential that one final representation exists for each unique datatype.
3. Identify Component Dependencies
After the components have been identified and reorganized to prepare for the migration, the system
architect should identify the dependencies between the components. This activity can be performed
using a static analysis of the source code to search for calls between different libraries and datatypes.
There are also several dynamic-analysis tools that can analyze the usage patterns of an application
during its execution to provide an automated map between components. Figure 2 below shows an
example of a map of component dependencies.
1. Split the code and convert delivery management into a separate, loosely coupled module
within the monolith
2. Split the database and define a separate schema for delivery management.
3. Define a standalone Delivery Service
4. Use the standalone Delivery Service
5. Remove the old and now unused delivery management functionality from the FTGO
monolith
Before looking at each of these steps, let’s first review the AS-IS code.
In the FTGO monolithic application, Order management and Delivery management are intertwined as
shown in this diagram.
The first step is to split the code and convert delivery management into a separate, loosely coupled
module within the monolith.
Step 2: Split the database
The second step is split the database and define a separate database schema for the ftgo-delivery-
service module.
The fourth step of the refactoring process is to use the standalone Delivery Service.
Step 5: Remove the delivery management functionality from the FTGO monolith
The fifth step of the refactoring process is to remove the now obsolete delivery management logic from
the monolith.
The microservices architecture deployment can be a good solution to this challenge. It is based on the
idea of extracting large components into multiple, self-sufficient, and deployable functional entities
grouped by a purpose. Each of these components is responsible for its own specific functions and
interacts with other components through an API.
According to Dzone’s research, 63% of enterprises, including such giants as Amazon, Netflix, Uber,
and Spotify, have already implemented microservice architectures. This widespread microservices
adoption is driven by many advantages such as improvements in resilience, high scalability, faster
time to market, and smooth maintenance.
However, migration to a microservice-based ecosystem may turn to be quite challenging and time-
consuming, especially for organizations that operate large and complex systems with a monolithic
architecture. The fact that microservices can peacefully coexist with a monolithic app gives a good
trade space for such cases.
First of all, it allows you to stretch the microservices migration process (and corresponding
investments) in time thus reducing immediate cost & effort loading, and stay fully operational through
the whole process. Moreover, you don’t necessarily need to move the whole solution to microservices.
It may be a good idea to go with a hybrid strategy when you extract only those parts that become hard
to handle inside the monolith and keep the rest of the functionality unchanged.
Sigma Software team regularly helps our clients with such assignments and we have already
experienced first-hand that with common sense, proper skill, and competent planning refactoring to
microservices becomes quite a feasible task that can substantially simplify further development and
maintenance activities.
WHY MIGRATE MONOLITH TO
MICROSERVICES?
Well-designed and implemented microservices can help address typical monolithic architecture
problems such as system scalability, slow delivery process, defect detection & verification difficulties.
With software components tightly interrelated inside the monolithic architecture, changes to a single
line of code affect the whole application.
Thus, even minor system modifications require re-deployment of the entire system and turn new
features release and bug fixing into a difficult time-consuming operation. Same with system scalability
– you would need to scale the whole monolith application, even if just a small part of the system with
specific functionality needs scaling.
A microservices architecture addresses these problems by breaking your large application down into
the smallest possible parts segmented by specific domains that communicate with each other through
lightweight APIs and look like a single application to end-users.
Microservices are stored independently of each other, which means that each of them can be created,
deployed, tested, or updated separately (and by different teams). If one of the components fails, the
development team can promptly switch to another version of this component and the application will
continue to run effectively.
From an architecture perspective, the major difference between the monolithic and microservices
approaches is the following:
As you can see, the Microservices approach turns your app into a set of independent
components providing more flexibility to your development process and resulting in such
benefits as:
However, once you replace the monolith with a set of independent components, integration
between those becomes a great challenge. This aspect requires special attention to both the
design, implementation, and testing part of the migration process.
As with any project related to architectural changes, the transition from monolith to microservices
requires thoughtful and careful planning. Our core recommendation here would be to gradually cut
off parts of the functionality from the monolith one after another, using an iterative approach and
keeping those parts relatively small. This will result in reducing migration risks, more accurate
forecasting, and better control over the whole project progress.
An excessive surge of optimism and desire to gain all the promised benefits as soon as possible may
prompt to move towards a seemingly faster Big Bang rewrite when functionality for all microservices
is being rewritten from scratch. However, we recommend thinking twice and go with the approach
suggested above. It may take a bit longer, but is definitely less risky and more cost-efficient in the
long run.
Of course, there can be cases when parts of the functional entities inside the monolith software are so
tightly interrelated that the only option to run those as microservices is rewriting. However, such cases
rarely cover the full functionality. So, we usually suggest extracting everything that can be isolated
first and only after that taking the final decision on the remaining functionality. It can either be
rewritten into separate microservices from scratch or left within the monolith which will become far
easier to maintain after most of the functionalities have already been detached.
With the suggested approach, a typical process would include the following steps:
Planning
Iterative implementation (this set of steps is repeated for each microservice from the backlog)
Designing the microservice and changes according to CI/CD & testing process
Setting up CI/CD
Implementing microservice
Competent planning and design are keys to smooth migration, as they significantly increase the
chances of success. At the same time, it is also important to pay reasonable attention to the
implementation itself and make sure you have available specialists who will take care of your entire
migration process including integration of the dedicated functionality with the monolith and thorough
testing of each release.
First of all, you need to create a backlog for microservices adoption. This requires identifying
candidates (parts of the functionality that should be turned into separate microservices) and prioritizing
those to form a queue for further migration. Ideally, those candidates should be functional groups that
can be isolated and separated from the monolith with less effort and solve some pressing problems
your app already has.
Of course, the most obvious candidate is an area that has certain performance and resource utilization
issues, or a domain area that will unblock other microservices’ separation from the monolith (e.g. some
functionality that is widely used by other parts of the monolith).
However, if you are looking for a more inclusive approach to determining what can be decoupled from
the monolith, here is a list of strategies you can apply during your migration to microservices:
Presentation layer – elements that manage HTTP requests and use either a (REST) API or an HTML-
based web interface. In an application with a complex user interface, the presentation layer is
frequently a significant amount of code.
Business logic layer – elements that are the nucleus of the application and apply business rules.
Data access layer – elements that access infrastructure parts such as databases and message brokers.
o In this strategy, the presentation layer is the most typical candidate that can be transformed
into a microservice.
o Such decoupling of the monolith has two main advantages.
o It allows you to create, deploy, and scale front- and back-end functionality independently from
each other.
o Besides, it also allows the presentation-layer developers to quickly iterate the user interface
and smoothly carry out microservices testing.
o Nevertheless, it makes sense to split this layer into smaller parts if the application interface is
big and complex.
Same works for the business logic layer and data access layer. Any of those is often too large to be
implemented as a single microservice as it would still be too big, complex, and pretty similar to the
monolith you already have. So, you would need to split those further into smaller functional groups
using any of the following approaches.
DIVIDING THE APPLICATION INTO THE DOMAIN AREAS
In case some functionality inside the application layers can be grouped into certain domain areas (e.g.
reporting or financial calculations), each of those domain areas can be turned into a microservice.
Since the functionality inside one domain area often has more connections inside the area than with
other domain areas, this approach quite accurately guarantees a good degree of isolation. This means
you will be able to separate this functionality from the monolith with fewer difficulties.
In the case of grouping, dividing functionalities into domain areas does not look like a fair alternative,
it makes sense to consider grouping the functional entities into a microservice by the non-functional
characteristics they have in common.
If a part of functionality calls for a fully independent lifecycle (which means committing code to a
production thread), then it should be decoupled into a microservice.
For example, if the system parts grow at different rates, then the best idea is to split these components
into independent microservices. It will enable each component to have its own life cycles.
The load or bandwidth can also be the characteristics by which you can distinguish a module. If the
system parts have different loads or bandwidth, they are likely to have different scaling needs. The
solution to this is splitting these components into independent microservices, so they can scale up and
down at different speeds.
For example, functionality that depends on an external service that is not likely to comply with your
accessibility goals. You can turn it into a microservice to insulate this dependency from the rest of the
system and embed a corresponding failover mechanism into this service.
Once you have identified the list of candidates to separate into independent microservices, you need
to carefully analyze not only the candidates but also the interdependencies between those. As a result
of this analysis, you will be able to create the backlog and define the order in which you will migrate
the candidates. In some cases migrating certain functionality into a microservice can greatly simplify
and speed up the process of migrating subsequent components. This should also be taken into account.
When developing a monolithic to microservices migration strategy, it also makes sense to plan the
availability of resources required for migration – either in-house or outside the existing development
organization.
MICROSERVICE MIGRATION STEP-BY-STEP
Once you have figured out which components and in which order to extract, it is time to pick up the
first candidate in your queue and project further actions – define the refactoring strategy, design the
microservice, estimate the efforts, and plan the iteration. It is also important to remember that the new
approach would require certain changes in your CI/CD and testing processes, so it makes sense to
agree on those changes with your team and adjust your processes accordingly.
The next step is to select the right strategy to migrate the chosen functionality into a separate
microservice. Two proven strategies we normally rely on in our projects are:
Strategy Preconditions:
Isolating functionality inside the monolith with subsequent Too many connections with other functionality.
separation into a microservice. Active development & a lot of changes inside
Implies the gradual removal of connections with other the monolith that affect the candidate.
functionality while keeping the candidate inside the
monolith until it’s ready for separation.
Once all the connections are removed and the new API is
designed, the candidate is separated from the monolith and
launched as a separate microservice.
Copying the functionality and turning the copy into the Need to have more flexibility in the
microservice Implies creating an independent copy of the microservice development process (the
functionality that is further developed into the microservice microservice is implemented independently,
while the initial functionality still remains operational can have its own lifecycle, and be built &
inside the monolith. Once the microservice and its deployed faster).
integrations are fully implemented and tested, the initial Low probability of changes to the candidate
functionality is deleted from the monolith. while the microservice is being implemented
(otherwise, you’ll need to carefully transfer
corresponding functional changes to the
microservice)
Both strategies have proven their efficiency. However, they both also have one vulnerability in
common. Once the migration process is done in parallel with active development, new dependencies
may emerge between the monolith and candidate functionality while the new microservice is being
implemented.
In this case, you would need to carefully track and resolve those through new API endpoints or through
shared libraries with common parts of a code. And on each occasion, this would take more time and
effort than if the functionality was still a single whole with the entire monolithic application.
STEP 2: DESIGNING MICROSERVICES ARCHITECTURE AND CHANGES TO CI/CD &
TESTING PROCESSES
First of all, you need to design the future microservice and define changes that should be brought to
your CI/CD & testing processes. This important step forms the baseline for further successful
migration of the microservice.
determine the exact code in your app that will be attributed to the microservice
design API interfaces through which the microservice will interact with the monolith and other
microservices>
choose a stack of technologies that will be used for the implementation of communication with
microservice (in certain cases, operational demands dictate the choice of a specific technology, but
most commonly, your development team can opt for their favorite tech stacks).
You also should bear in mind that with the transition to microservices, you also will need to make
some changes to your testing strategy for microservices. When a microservice is launched, testers
should take into account that now they can interact directly with each microservice. The test team
would now need to look for issues at the levels of microservices interactions and infrastructure
(containers/VM deployment and resource use) as well as at the overall application level.
Each of these levels requires its own set of testing tools and resources. Test automation can be of much
help in this case. With the right microservices testing strategy in place, your team can seamlessly
migrate to microservices at the optimal time and without hassle.
STEP 3: SETTING UP CI/CD
The process of changing CI/CD can be started concurrently with microservice development, or even
before its implementation so that you can successfully maintain microservice during its development
and after it is launched.
Continuous Integration will allow you to easily integrate your code into a shared repository.
Continuous Delivery will allow you to take the code stored in the repository and continually deliver it
to production.
There is an even more effective way. CI can transform code from a repository to a compiled package
(ready to be deployed) and store it in Artifactory. CD can deploy compiled packages from Artifactory
to the destination environment. In this case, CI and CD processes are independent of each other, don’t
have intersection steps, and could be changed/improved independently of each other.
The time has come to implement everything that has been planned. At this stage, the development
team encapsulates the functionality into an independent microservice, cuts off the tightly-coupled
connections with the parent application, and implements the API gateway that becomes a single point
through which the microservice can communicate with the parent application and other microservices.
Thorough testing should be an integral part of the microservice implementation as you need to
safeguard that the microservice works exactly as expected and all possible errors or problems are
tackled before the new environment is launched. Test coverage development can start in sync with
microservice implementation (or even in advance when it goes about tools and test types selection and
planning) to monitor the results of the microservice as early as possible (this can make test results
more accurate). Testing should be considered completed when the microservice operates flawlessly in
the new environment.
The final result of the implementation step is a fully-functional independent microservice that operates
in isolation from the monolithic application. With proper and detailed planning, deployment of
microservices is completed as forecasted and does not create any delays or hindrances in the overall
development process.