Monoliths vs Microservices
This article introduces some of the core differences between Microservices and Monoliths.
Introduction to Microservices
Let’s kick things off with a formal definition:
Microservices is a variant of the SOA (service-oriented architecture) architectural style that structures an application as a collection of loosely coupled services — Wikipedia
You’ll notice the reference to SOA. Believe it or not, the idea of Microservices has been around since 1994. Back then it was coined SOA or “Service-oriented Architecture”. But, to save you from a boring detailed explanation of the past, Microservices is, in a nutshell, SOA 2.0 or SOA next. The strategy is to take a complex application that contains heavy amounts of code, design and business logic, and convert it into small, fine-grained services.
The Monolithic Application
To understand what constitutes a Monolith application, take a look at the following diagram:
In this example, we have a Single Page Application that’s primarily made up of 3 CRUD Processes:
- Purchase Requisitions
- Credit Requests
- Credit Requests
- Leave Requests
Each of these processes require Create, Read, Update and Delete functionality. They are also driven by rules and business processes, making them more complex than a typical Reminder or To Do app. Single Page Applications have been a favorite for modern web application development, providing users with a responsive and optimized interface. Now, not all SPAs are considered Monoliths. To this day I highly recommend a SPA strategy. The issue is how the design is put together.
It’s a Monolith Application because…
Using the example application above, we could easily call it a Monolith because:
- All logic exists in 1 design
If you have 3 developers who are each responsible for a CRUD process, they’d have to carefully apply changes, bug fixes and enhancements to the design. If not done right, they risk code conflicts or worse, overwriting of someone else’s code. Sometimes developers even land up waiting for others to finish certain tasks before they can continue.
- Duplication of functionality
This unfortunately happens a lot with multiple developers working on the same design. Each process requires functionality similar to the others. What happens is, functions and logic get created uniquely for each process. Then, later on in the development cycle, the team realize that a lot of the logic can be centralized and reused.
- Client-side and Server-side communication happens natively
Because the application doesn’t have to look anywhere else for logic, most calls to the server-side happen locally and not via an API. If a separate application wanted to query one of the CRUD processes above, it would have to exist on the same server and platform. In other words, our application is silo’d from everything else.
- Unnecessary scaling of application
In the example application above, let’s say only 1 of the CRUD processes gets used aggressively, while the other 2 are rarely used. To provide enough machine resources, you would need to scale the entire application.
These are just some of the reasons why we would call our application a Monolith. Trust me there are more, but I think i’ve made a good enough point? Next, we’re going to see what our application would look like if we developed it using a Microservice strategy.
The Microservice Alternative
A common misconception of a Microservice strategy is that the entire design needs to be converted in one go. This is not true at all. The beauty of Microservices is that small steps can be taken in phases, leading to the end goal of having a complete Microservice architecture in place. As an example, let’s divide our Monolith into 3 phases:
- Phase 1: Separate Client-side from Server-side
To separate the UI and client-side design from the server-side logic is a perfect start. Even if you have no API endpoints for the client-side to connect to, depending on your application platform, you could still have the client-side separated, but keep it on the same platform/server. This way the calls would still happen locally. Obviously there are many application platforms out there and each one has their own way of doing things, but if you have to look carefully, I’m sure you would find a way to implement this kind of phase.
- Phase 2: Everything is an API
This is where things get a bit more involved, but pays off dividends in the end. Expose all the core functionality on the server-side as API endpoints. An example set of APIs for each process could be as easy and fulfilling the CRUD process flow:
- Saving new or updating existing records
- Deleting 1 or more records
- Fetching records based on certain conditions
You may have certain feature functions for each process that related to that specific process. For example, for the Leave Requests, you could have a function that checks available leave for an individual. Wouldn’t this be awesome as a consumable API not only for the current application, but for other applications as well?
Here’s where it starts getting fun. Now that each of the CRUD processes’ core functions are APIs, we can split the server-side logic for each of these processes into separate services. Using the example application above, you could now have 4 server-side runtimes:
- Core Server-side Runtime — Responsible for core functionality that gets used by all 3 CRUD processes (Approver Roles, Business Process Management, Other Random Functionality)
- Purchase Requisitions Runtime — Logic only relevant to Purchase Requisitions, but still available as web services
- Credit Requests Runtime — Logic only relevant to Credit Requests, but still available as web services
- Leave Requests Runtime — Logic only relevant to Leave Requests, but still available as web services
Even if at this point, some of the logic is still duplicated, you’ve ended up with 4 runtimes that can stand by themselves and easily communicate with each other using APIs. You are well on your way to implementing a Microservices architecture.