Microservices - Getting started
Thanks for the overwhelming response to my newsletter Overflow! Here’s the first post on Microservices - let’s get started!
"Hundreds of files with tightly coupled business logic! The search engine, transaction management, payments and other complex functionalities of an application were bundled in the single codebase. I had to make one quick change in one module and get away with it! Most of my time was spent in finding the correct function, and running unit and integration tests since each of the dependent functionalities needed to be checked. The coding part took just a few minutes. I was still not confident in deploying my change in production because of the complexity and inter-dependencies between different modules." -- new employee in the team
Let's get into the details of the above problem and understand more.
Consider an example of any online business presence. You need a user interface, a business logic layer, a data interface layer, and a database to get up and running with your website.
User Interface: A user interface is the front-end interactive part of the application. The users can interact with your website using neatly built user interfaces (UIs) in HTML, CSS, and JavaScript.
Business Logic: This is the main building block of an application. It takes in some input, processes it, and fetches/modifies the relevant records in the data store. You can use any of the programming languages like NodeJs, Python, Java, PHP, etc to implement this layer.
Data Interface Layer: This part makes it easy to communicate with the database. The business layer talks to the database using the data interface layer. However, this layer is optional and the business layer can directly communicate with the database.
Database: This is where all of the data is dumped! You can use a structured relational database like MySql to store the data. It is also common to use NoSQL databases like MongoDB and Cassandra.
You need to build just these 4 layers to showcase your idea to the real world!
Jack is a web developer and can code a website in a few days. He wanted to start up his side gig of having an online gift store.
He built the four application layers; the user interfaces using HTML, CSS, and JavaScript; the business logic using Java; the data interface layer using hibernate and used MySQL database. He purchased a domain giftstore.xyz and hosted the application code on an application server and the MySQL database on another server.
The website was neat and interactive and a few users started buying gifts. Everything was working as planned!
The number of users started increasing to some thousands in a few days. Jack added a few more interesting features including a recommendation engine to the application and the users loved his website!
He also added analytics to track the activities of the users and he was logging this data in one of the tables in the MySQL database.
It was an easy job to add/tweak any feature of the application. Jack's life was easy back then!
The number of daily transactions increased rapidly and the number of users grew to millions in a span of a few months. Jack started noticing slowness in getting back the response from the application and database servers. One of the SQL queries was taking more than 5mins (300000ms) to fetch records from the database.
Users started complaining about the poor experience on his website. The thousands of requests were hitting the server at the same time and the poor server couldn't handle more than its capabilities.
Jack tried many ways of improving the performance of his website but none of them succeeded. The application code has become a lot more complex and it is very difficult to track down even a simple bug.
The deployment of the application on various servers was also a pain. He used to deploy his application code manually on each of the servers and the corresponding server was down until the deployment activity is completed.
Jack pondered on the fact that some of the big companies like Facebook and Netflix have 100 times more traffic than mine and they are working just fine! He knew something is wrong with the way his application is built and deployed.
The 4 bits - user interface, business logic, data interface layer, and database are not enough to build a magical application! You need to add more pieces to build a scalable, reliable and responsive application.
Jack decided to hire an expert to resolve this issue. He hired Bob to help him build just the right application that would help him generate loads of money from his gift store. Bob investigated the application and listed this problem:
This application follows a monolithic architecture pattern. Let's refactor this to microservices architecture and have different services and databases to handle different functionalities.
"Ergh! What is a monolithic and a microservices architecture pattern? How do different services handle different functionalities and how do they communicate with each other in the first place? Different databases, really?" exclaimed Jack.
In this section, we're going to look at how Bob resolved the issues of Jack's gift store.
Here's a quick overview of the topics that we'll be learning in the first 3 posts on Microservices:
Monolithic Architecture Pattern - Old way of building applications
Problems associated with the monolithic applications
Introducing Microservices
What are loosely coupled services?
The Transition from Monolith to Microservices Pattern
The Scale Cube
How do Microservices solve the problems associated with monolithic applications?
Deep Dive into the Microservices Architecture
Additional overhead in creating a distributed system
How to divide an application into different services?
Communication between different microservices using HTTP and gRPC
How to reuse code across different microservices?
How to set up Authentication and Authorization in the microservices architecture?
Increased memory consumption
Deployment-related issues while adopting microservices
How to maintain data consistency?
How to query different databases?
Using Sagas for distributed transactions in Microservices
How to structure microservices?
Don't worry if any of these topics don't make sense! We'll learn everything from the basics and eventually connect these pieces.
Let's get onto the microservices voyage!
If you have not subscribed to Overflow, consider subscribing using the link below to get future Overflow posts in your inbox!
Monolithic Architecture Pattern - Old way of building applications
A monolithic application is designed as a single-tiered architecture. It is self-contained and is not dependent on any other application/program for carrying out any function.
The entire codebase including the user interfaces, business logic, databases, analytics, and reporting is part of the same application. It is a common practice to have only one database to dump in all of the data.
The monolithic applications are not modular and the different functionalities are tightly coupled. If you have to make one change on the payments page, you'll have to test and deploy the entire application code.
The inter-dependencies between different modules make it difficult to implement even some quick fixes. You cannot scale monolithic applications beyond a point. There is only one database that has grown to store billions of records. Got to shard them, and store them in different clusters! Eh? What is that? Hang tight! We'll be learning these concepts in a moment.
Problems associated with the monolithic applications
Let's start by identifying some of the common problems with the monolithic applications:
Difficult to add new features
The monolithic applications are easy to build, test and deploy in the initial phase. But they become challenging to handle as the size and the complexity of the application increases. Developers spend a lot of time trying to figure out the impacted code areas after adding just a single line of code. This hampers productivity and also makes it difficult to add new features.
Intimidating for the new members of the team
The codebase is heavy with millions of lines of code. This is intimidating for the new members and they may take some time to understand the different parts of the application. The product development cycles may be delayed.
Scalability
If the load on a single application instance increases, we can increase the number of instances and can use a load balancer to equally distribute traffic among different instances. However, all of these instances access the same database. We cannot scale the application with an increase in data volume.
Some components of the application may be CPU intensive and some may require more memory usage. However, we cannot scale different components based on their specific requirements as these components are tightly coupled and are dependent on each other for their execution.
Dependent Teams
The goal of small and autonomous teams cannot be achieved with the monolithic architecture pattern. The different teams depend on each other for the development and deployment of the different business capabilities of an application.
Difficult to update the technology stack
The monolithic applications use the same programming language to implement all of the functionalities. If you have to update the version of any package dependency, you'll have to check its compatibility with all the dependent parts in the application. It gets challenging to keep up with emerging technologies and frameworks.
Difficult to track bugs/errors
The entire application is down if there is an error in one part. It is difficult to track complex bugs in such a big pile of code.
Dependent Deployment Cycles
The entire application needs to be deployed even if the changes were in just one small part of the application. This creates dependencies between different teams for deploying quick fixes. The continuous integration is turned into a liability because deploying a few fixes means deploying everything at once!
Now that you know monolithic applications cannot scale beyond a point and are not suited for big complex enterprise applications, let's look at an alternative architectural style.
In the next post, we’ll understand more about microservices architecture, scale cube, and how microservices solve the problems associated with monoliths.
Please note: Microservices architecture is not suitable for all the applications and you might not even need to implement microservices at the start of your journey. It’s always a good idea to start with a monolith and let it grow until you realize you cannot scale it beyond this point. That’s when you should hop onto microservices architecture and get to solving real monolith problems.
Thanks for reading!