Continuing our journey on learning more about Microservices, today, we’re discussing the differences in following Monolithic and Microservices architecture patterns. As we’ve already investigated what microservices are, let’s start with the concept of the monolith.
Building a Monolithic App
A monolithic architecture pattern implies that you build an app as a single-tiered and self-contained software unit without modularity. Here all elements of the app’s code represent a single layer from a single platform, independent from other applications.
The design philosophy behind the monolith approach is to make the stand-alone software responsible for a full cycle of designated functions, not just for one particular task. Take, for example, personal finance management apps and word processors — most of them had been using only the monolith basis until recently.
Following Monolith Pattern
Let’s say, your project is to build a shipment web app. At first glance, this software has to have such features as user registration and management, orders form, logistics options, routes maps, price and delivery time calculators, invoice generator, various payment options, and admin panel. And once a shipment company receives an order, the app has to present admin with the list of matching delivery options, contacts of responsible workers, and so on.
The monolith formula is to model the app first. A team of business analysts, PMs, engineers, and architects create a project roadmap of how things are going to work at the scale. Where each form goes, what will be the user stories, business logic, how many shipment categories they will have, etc. A database will be responsible for storing all the app’s information; there also has to be an easy-to-navigate, intuitive user interface. So, basically, we have a frontend side that communicates with a backend side.
This backend is deployed exactly as a monolith with its own attached database. The app can also communicate with third-parties APIs, and it supports a full range of devices (desktop, mobile, tablets). Sounds familiar? It’s because we’re very accustomed to this architecture. It’s been working efficiently for us for a long time.
Why? Due to its simplicity in many fields:
Simple Development — your overall tech stack on the project will be only a few languages, frameworks, and databases; a single IDE is usually enough for your monolithic code.
• Simple Testing — having a monolith, you mostly test a single piece of software, with the occasional need to mock several external services for integration testing.
• Simple Deployment — you just need to deploy the final artifact of your monolith (WAR file or directory hierarchy) on the suitable runtime and configure the designated properties.
• Simple Scaling — you can scale and improve the app’s availability by running multiple copies of the software behind a load balancer.
Then why even bother about microservices? As with anything, there are two sides to the coin. Along with benefits, the monolith has a number of drawbacks. For instance, you might want to empower your shipment app with additional features. The app’s codebase has to grow by who knows how many lines. The team grows in size since you probably add more needed engineers. Here come the problems.
The bigger your app becomes, the harder for each new developer to show productivity right from the first days. It’s because the app’s codebase gets challenging to sort through or modify. Not to mention that you might need to set up several teams to maintain the particular functionalities of your massive monolith — for design, for frontend, for backend, for QA, for deployment. And these teams can’t work independently; they must coordinate their actions and efforts to meet each other at the redeployment stop.
So, at this stage, the drawbacks of the monolithic approach become more visible and significant:
The code has an enormous number of lines that not every developer is able to navigate through;
• New developers’ productivity is low;
• Monoliths have limited reusability;
• The app is difficult to modify and update, which slows down the development phase;
• The IDE overloads and becomes slower, less productive;
• The technical debt increases over time, but the tech stack must remain the same as at the start of development;
• Due to the lack of modularity, the code’s quality declines with each change being made;
• Continuous deployment of an entire app becomes harder and harder as the code grows;
• The operational agility collapses because of the frequent redeployment rounds;
• Scaling gets more complicated, one-dimensional due to the increasing volume of data — caching is less effective, memory consumption increases, and the app gets slower.
Just imagine, you need to change the database for one small part of your app. What to expect? A lot of redevelopment for several app’s sections, not just for that little feature. This comes along with huge risks of scaling the entire piece of software, slower deployment, and inability to predict if the redevelopment will even work.
Building a Microservices-based App
So, what about the microservices pattern? Business-oriented APIs represent their capabilities. Simply put, each microservice carries out a core business capability, a valuable asset to the business. These assets are highly scalable to be used in multiple contexts, without direct binding through must-have languages or shared libraries. Each service is reusable for more than one business process and over different business channels.
As the microservices are loosely-coupled, the dependencies between them and their end-users are minimal — the consumers won’t feel any changes in the implementation of the particular service. Combining robust API management and up-to-date cloud deployment and integration technologies, the microservice architecture pattern divides the monolith into a set of fully independent software units. This principle of modularity allows updating, modifying, and replacing the whole systems or service components, without any impact for end-users.
Therefore, the microservices bring the following benefits for your application:
• Principle of modularity increases the overall agility of the development process;
• High modularity, scalability, and reusability of the app’s logic parts;
• The development tech stack is unlimited;
• Easy code maintenance — fixing, modifying, and replacing the parts of your app doesn’t require wholesale redevelopment;
• Scalable and independent engineering teams — services are small, so you need only a handful of developers;
• New developers’ productivity is higher due to the smaller codebase;
• Services are independently developed and deployed artifacts, which significantly speeds up the time to market.
Summarizing
So, what about the microservices pattern? Business-oriented APIs represent their capabilities. Simply put, each microservice carries out a core business capability, a valuable asset to the business. These assets are highly scalable to be used in multiple contexts, without direct binding through must-have languages or shared libraries. Each service is reusable for more than one business process and over different business channels.
As the microservices are loosely-coupled, the dependencies between them and their end-users are minimal — the consumers won’t feel any changes in the implementation of the particular service. Combining robust API management and up-to-date cloud deployment and integration technologies, the microservice architecture pattern divides the monolith into a set of fully independent software units. This principle of modularity allows updating, modifying, and replacing the whole systems or service components, without any impact for end-users.
Therefore, the microservices bring the following benefits for your application:
• Principle of modularity increases the overall agility of the development process;
• High modularity, scalability, and reusability of the app’s logic parts;
• The development tech stack is unlimited;
• Easy code maintenance — fixing, modifying, and replacing the parts of your app doesn’t require wholesale redevelopment;
• Scalable and independent engineering teams — services are small, so you need only a handful of developers;
• New developers’ productivity is higher due to the smaller codebase;
• Services are independently developed and deployed artifacts, which significantly speeds up the time to market.
Following Microservices Pattern
So how to build your shipment app using the microservices? It all starts with the design phase, where you need to define the structure that will present the app’s functionality as a set of loosely-coupled services. This means identifying the subdomains of your main domain, which correspond with particular services that represent different parts of your business.
Exploring the main domain of the shipment services, we can roughly subdivide it into 3 subdomains:
• The user subdomain that will deal with user authentication and management.
• The order processing subdomain that will be responsible for the full cycle of orders processing — price calculation, invoices, discounts, and delivery options.
• The admin subdomain related to the supplier’s side and management of workers responsible for delivery.
Now you have to have clear connections between these subdomains. Remember, your microservices should be totally independent of each other. So, you need to follow the domain-driven design techniques and patterns to design an appropriate model. First, you should duplicate the features that actually depend on each other in all subdomains to make them separate entities. Then, each subdomain will process its own data on its own terms, so there have to be separate databases for each of the three. Avoid sharing databases — it’s bad manners in the world of microservices.
When these things are cleared up, you’ll end up with a map of services for each domain that can be put into design and production. Having small parts of the software to develop, test, and deploy, your team will work faster and more productive, avoiding overloaded IDE, limited tech stack, and overall chaos related to redeployment.
Summarizing
Of course, both architecture patterns have their advantages and drawbacks. Microservices are not panacea for any project, but it’s definitely a good choice for large, complex software. Small teams, higher agility, easier management, faster development cycle… Is it the best fit for your project? The choice is yours. In the following articles, we’re going to explain all the elements in the microservices ecosystem to help you determine when using this architecture is smart.