When developing an application, we as software engineers face different types of architecture for building an application. Among them, Monoliths and Microservices are considered to be the latest trends.
When designing an application, we can consider both, monolithic or microservice-based architectures. What we will choose depends on how the structure of an application is contoured by functional requirements and available resources. However, each architecture has a set of trade-offs, that need to be thoroughly examined before deciding on the final architecture of the application.
These trade-offs cover development complexity
, scalability
, time to deploy
, flexibility
, operational cost
, and reliability
. I’ll try to explain each trade-off below…
Development Complexity:
Development complexity
refers to the effort required to deploy and manage an application. So deployment and managing a monolith or microservice-based application are different so let’s discuss those development complexities for monoliths and microservices-based applications.
Programming Language
Monolith: The monolith application usually revolves around a single framework or programming language. For example, we can say we’ll build a Booking application using the Django web framework(which depends on python programming language).
Microservice: The microservices application can be deployed using multiple languages. For example Same Booking application we considered above, let’s say the application has two services Login
& Book ticket
.
We might consider building the Login service
using the Go programming language, on the other hand for Book ticket
we can use Python.
Management of Code Repository
Monolith: The monolith application usually belongs to a single code repository.
Microservice: Each Microservice requires its own separate code repository.
Note: We might think it’s better to manage a single repository than many, but the development complexity scale radically when more functionality is added to the project. As for microservice we team can work on a functionality independently as all functionality has its own codebase.
Development Cycle
Monolith: The Development Cycle of a monolith application is sequential as a single team will work on the codebase at a time. Also, the team needs to be careful about adding new functionality as the team may need to modify multiple functions to ensure backward compatibility.
Microservice: The Development Cycle of each microservice is concurrent as each team works on each functionality separately on their own codebase.
Scalability:
The term scalability
refers to how an application scales up and down when the incoming traffic increase exponentially or there’s a sudden demand on a particular functionality of the application.
Traffic
Monolith: As the traffic increases, we need to replicate the entire unit with all the functionalities. For example, if we need to scale the Payment functionality, we need to scale other functionalities respectively.
hence this is heavy on resource consumption such as CPU and Memory.
Microservice: As the traffic increase on a particular functionality or service we can easily identify which service or functionality needs to scale and only scale that up will solve the traffic issue.
Resource Consumption
Monolith: As the traffic example describes, we can conclude monoliths are heavy on resource consumption such as CPU and Memory. As we are using more space than actually needed.
Microservice: As each functionality belongs to a separate service in a microservice application, the resource consumption is on-demand which means is, we need to scale up to the current need for the moment if we don’t need that in the future, we can easily scale down.
Time to Deploy:
The term Time to Deploy
refers to building a deployment pipeline and deploying new features or bug fixes easily.
Delivery Pipeline
Monolith: As in the monolithic approach only one delivery pipeline is needed to deploy the whole application. As we deploy the whole application each time it came with high risk with each deployment as if the deployment didn’t work leads to a disastrous situation if the release fails as it’ll take down the entire application which means there is a higher risk of violating the zero downtime principle, which aims that application should be available to the end-user/consumers 24/7.
Microservice: On the other hand, each service needs its own delivery pipeline so that we can deliver each service individually. As we add a new feature to a specific service only that service needs to be deployed and if that service deployment won’t go smoothly as we expected, only that service will go down but the application should be available to the consumers. As such, there is less risk to take down the entire application with each release. Consequently, Microservices allow an increased velocity of feature development as we can have more releases with less risk.
Flexibility:
The term Flexibility
refers to the ability to incorporate new technologies and adapt to new principles and tooling. There are moments when it’s better to use a different programming language before a specific feature or modify our application for a specific platform.
Monolith: In Monolithic application, this type of modification may lead to rewritten or restructured the entire project prior to that new technology need. This leads to the conclusion that monolith application has a low flexibility rate.
Microservice: On the other hand, each service is purposely built to be loosely coupled and allows independent changes to services as we writing or restructuring one functionality is more achievable than rewriting the entire stack.
Operational Cost:
The term Operational Cost
refers to the necessary resources to build, deploy and release a product. A low initial cost to spin up an application is certainly inciting.
Monolith: This is the case we all observe in the monolithic architecture where only one code base is needed and there’s only one delivery pipeline to deploy the entire application.
Microservice: On the other hand, each service requires the maintenance of multiple codebases and delivery pipelines and the dependencies of maintaining different programming languages if we require to use them in a microservice.
This scenario demonstrates the whole application/product development process. The situation is flipped when new functionalities are added and the application needs to be scaled to cope with high customer demand.
Note: Maintaining a monolith over time imposes more complexity and consumes more resources when replication, as we all know maintaining an application, is more costly than developing one from scratch. On the other hand, the Operational cost for a microservice is directly proportional to the required resource at the time. Scalability is performed on each service and adding new components is a defect to operation in an application which makes microservice more scalable than monoliths.
Reliability:
The term Reliability
refers to the ability to recover from failure and waste monitoring the application all the time.
Monolith: If an application goes to a failed state the entire stack will need to be troubleshot and recovered for availability.
Microservice: A distributed amount of functions that interact with each other via the network calls. If a component fails then only that component will need to recovery.
Note: In Microservices, it is possible to have representative metrics and logs of a separate unit while with monoliths getting granular visibility for each functionality is difficult as all the metrics and logs for the entire application will be aggregated together.
If you like, you can read the same article on my Personal blog
You can read my other blog-posts Here
Summary
Trade-Offs | Types | Monoliths | Microservices |
---|---|---|---|
Development Complexity | |||
Programming Language | One language | Multiple languages | |
Management of Code Repository | One repository | Multiple repositories | |
Development Cycle | Sequential | Concurrent | |
Scalability | |||
High Traffic | Replication of the entire stack | Replication of specific service/functionality | |
Resouce Consumption | Overconsumption of resources | On-demand resource consumption | |
Time to Deploy | |||
Delivery Pipeline | One delivery pipeline | Multiple delivery pipeline | |
Deployment process | Easy | Comparatively harder | |
Deployment | Entrie stack deployment | Separate functionality deployment | |
Velocity | Low velocity at scale | High velocity at scale | |
Flexibility | Low | High | |
Operational Cost | |||
Initial cost | Low | High | |
Scaling cost | High | Low | |
Reliability | |||
Recovery | Recovery of the entire stack | Recovery of the failed component only | |
Visibility(metrics/logs) | Low | High |
In conclusion, we can say each application architecture has a set of trade-offs that need to be considered at the beginning of a project. But more importantly, it is paramount to understand how the application will be maintained in the future e.g. at scale, under load, supporting multiple releases a day, etc.
There is no Predefined/Golden rule
to build a product, but a good understanding of the trade-offs will provide a clear understanding of the roadmap to build the product goals we’re trying to achieve. Now regardless of what architecture we choose to build the project, as long as the project is coupled with an efficient delivery pipeline and design pattern, the ability to adopt new technologies and how easily we can add the feature to cloud-native deployment remains certain.