The Age of Monolithic systems
Monolithic architectural patterns
6 min read
Coming in first in the series; the monoliths of web applications - monolithic architectural patterns.
Broken down, a monolithic system is one that is deployed as a single unit of interconnected components.
Let's give an example.
We are creating a hotel management system for our local favourite spot in Django. Our application will hold, amongst other things, account management , rooms, rating and so forth. Break it into as many modular applications as you might come up with.
Essentially, a monolithic system is one in which multiple smaller applications live within the same server, share a file system and same database. Sound familiar? To easily identify if a project is monolithic, ask, can you scale an individual modular part of the application (memory or CPU) while leaving other apps unattended? If not, then this is a monolithic application.
Projects built like this are known to be simple in terms of development, easy to scale up to a certain cap, easy to deploy and be fast during the development phase. Fast, because all its components are together hence easy to collect and it would be written majorly in one language hence no need to learn another language/framework.
Have in mind , for instance, a java application using a jar file that contains all the apps logic for deployment.
A common architectural pattern , one we aim to discuss , that follows this , is the layered architectural pattern.
The Layered Architectural Pattern (n-tier architecture)
Governed by the Single responsibility Principle - a class or module has one and only one function - the layered approach leverages the separation of concerns. Broadly speaking, it splits software products into the presentation layer, business logic layer , persistence layer and the database layer.
These are modular sections of the application.
Each section/layer outlined has a specific function without needing to know what the other layer entails.
The presentation layer, for instance, deals with the display of information to the user. The business layer gets data from the persistence layer and processes it against application logic (say merge with another table or filter against a third party ) without the need to know anything about the user interface. Hence, you might as well change the presentation without changing the logic of what your project aims to do or change from an SQLite database to MySQL without your business logic requiring to know.
On a number of smaller applications, the business and persistent layer may be merged into one business layer. As observed, we get to have 3 layers in one case, four in another and so forth, hence the name n-tier, where n is the number of layers.
Requests flow from one layer to the next, say business logic to the persistence layer. This is a classic example of a closed layered system. That is, one in which, a lower-level layer cannot be accessed directly.
Not letting the user access the database without walking through the business logic. What is handled in this 'logic' you might ask?
For one, authentication and authorization. No one should just access our local hotel database. at the same time, an ordinary user should not be able to perform managerial actions within our platform. That is left to the manager and only the manager.
In its basic form, a layered architectural system works in a closed circle.
We may, however, need to have access to lower-level layers without the intermediaries - an open layered system.
Given the premise of the n-tier architecture, we have an extra layer, the services layer . Our project is now a 5-tier system. Despite having this layer, we might not use it as often. Hence, it does not serve our purpose for every request to go through it and have no change or mutation. What this layer would be adding to this specific request, is time; and time is of the essence. We have it, yes, but only use it when needed.
We bypass all that, and keep this layer open. This allows our request to access the level lower to it without having to go through it.
Within your project, aim to have at most one open layer. Having more will create interdependencies between these layers, and you lose the whole point of having the layered system in the first place.
Another variation of the n-tier architecture is the use of cached layers. Here, requests may be cached in between layers so that concurrent requests do not have to go all the way down. Plainly put, not every request made must hit the database (save the user some time).
Let's take our app idea from the top
A guest wants to make a reservation. They log onto our page and see that we have , on top of rooms, fun activities and games for our guests (the presentation layer). They go ahead to make a reservation. Our screen gets their request and sends it to our module within the business logic layer. Here, we want all available rooms and as a bonus, the activities available within the time frame of booking (notice the filtering of time, price and so forth). Now, call the room and activities data access object (dao) within the persistence layer, our ORM. These will, in turn, execute the queries necessary from the room and activity database table. Push this data back to the user and let them know they can have sushi on the house!
A little perspective
So, we've given a primer on monolith architectures using the n-tier approach. We have seen a case application that uses the monolith system and used it to get our new client a room. Our system works and our local hotel pays in kind. What happens when they get new outlets? More activities and more traffic? Will it scale?
Monoliths are good, and monoliths are great. How you use them depends on your needs. Use a monolith when you want simplicity in development, deployment and scaling , work in a relatively small team or are creating a prototype. That works 100%.
A monolith will, however, bite you as the system gets larger due to a number of reasons.
Framework /Language lock
The whole codebase is in one framework that might not be suited for future feature additions. For example: we wrote our application in one language that is good for speed but we now need a section of it to use image processing libraries. How suited is it for image processing?
Inefficient resource allocation
If one endpoint needs more time either due to IO operations or processing, other functions may be locked.
Scaling becomes difficult
1000 or so requests to the search functionality have been made. In monolithic architecture, a new instance of the running application might be created during load balancing to handle multiple accesses. Here, we have a whole new instance of the same application created, when all we needed was the search to grow .
Take it that we now have a chain of hotels across the town. Heck, across the country. We have added feature 1 and gotten to feature 300. We now want to change a variable name within an application in our project. We have not touched anywhere else but that section. The whole codebase, including the untouched , has to be compiled once more. We are still developing, remember that! That's 10 minutes for build and compilation that could have been better served elsewhere not to mention an IDE load time that puts us all to sleep.
So far, we have a birds-eye view of what we have been creating. A capture of what monoliths involve and what they do not - layer upon layer. Great start. Is there more? Stick around a while longer.