Monoliths vs. microservices
Conventional enterprise applications are often built in three layers: a client app, a server application, and a database. The server-side application receives client requests, executes logic, retrieves data, updates data, and responds to the client app. Typically, this server-side application is a monolith (a single executable software program). Even the smallest change to the system often involves rebuilding and redeploying the server-side application.
The modularity within a monolithic application usually follows the features and limitations of the programming language, and business processes shape the other aspects of application modularity. The monolith continues to grow as requirements change, and new functionality is built. As the growth continues, the burden increases to maintain and preserve a clean, modular structure. Sustaining insularity, moreover, isolating changes only to affect the relevant module(s) becomes difficult. One small change to the application requires a complete rebuild and redeployment. With no way to scale the modules independently, it’s necessary to replicate the entire monolith.
Here’s another dose of reality: most monolithic applications accumulate technical debt as they grow in complexity. Technical debt accumulates with mounting additional rework caused by choosing a series of workaround solutions that do just enough to get the job done. Under pressure to innovate and fix bugs, most teams opt to add more technical debt instead of using a better approach that takes more effort to implement in the short-term. When faced with the prospect of moving to a new microservice architecture, keep in mind the persistent challenges of maintaining and extending an inflexible monolith.
Problems presented by monoliths include:
A monolithic code base grows along with application functionality and fixes, which increases the load on the technology stack and the capabilities of the development environment.
Because a monolith consists of a single executable code base, it can be challenging to make changes to the technology stack.
The deployment and testing effort (even if automated) grows exponentially as the monolith increases in complexity.
Code refactoring becomes increasingly difficult because it’s hard to predict how it will impact application functionality.
If there is a failure in any single function or component, the entire application goes down.
Performance issues in one module can adversely affect the entire application. If a specific function suddenly consumes more resources, overall performance diminishes.
Scaling a monolithic application is commonly done by deploying it to additional servers (i.e., horizontal scaling). Each parallel monolith then places an equal load on the underlying resources, which is a highly inefficient design.
As an application becomes more complex, a monolithic architecture increases the burden on development and deployment. Developers in different teams come to realize the importance of the ability to isolate components. However, the monolith inextricably links all components, which virtually prohibits developers from working independently. It’s not possible to develop or deploy discrete functionality. This complete interdependence eventually drives developers to frustration.
Microservices offer high levels of flexibility and maintainability that are simply impossible for a conventional monolithic architecture to provide.