
Traditional monoliths
When building modern enterprise web applications, it's common to build it as a monolith. In this architectural style, applications can be built as modules which are bundled as a single deployable unit and executed in a single runtime process. Deploying a monolith can be done by simply packaging the application as a web archive (WAR) file and publishing the artifact to a server. Starting a project with the monolith approach is much easier than trying to build a service-oriented or microservice architecture from the start. This approach does have some benefits, such as the following:
- Monoliths are simpler to work with, as even though developers may not get the correct boundaries defined between the modules, it's not that hard to refactor
- Testing a monolith is simpler as there's less moving parts when compared to a service-oriented architecture
- Not much thought is needed for what to scale, as you can add more boxes and the entire monolith application will scale horizontally
- It's easier to enforce constraints on the team by means of centralised governance
Let's consider an example where you may be building an issue management system, which has various modules such as document manager, users, audit, and ticket management. Audit could be a web service API, but for convenience, it's bundled along with the same application. Here, the application would be deployed as a single unit (WAR) and run on a Java EE application server. If any of the modules are unable to cope with the load, then the entire application is scaled to meet this demand. Here's what a scaled monolith looks like:

While the monolith is good for simpler projects, it does have drawbacks when used for larger complex applications. It is critical for architects and developers to understand the benefits and drawbacks that each architecture brings with it. Here are the drawbacks of a monolith:
- As the code base grows, changes are difficult to make without impacting other parts of the system.
- Making changes in one area needs to be followed with testing the entire monolith, so the duration of an iteration can't be kept short.
- As both code and team size grow, it becomes a challenge for anyone to fully understand the application.
- Even for small changes, the entire application must be deployed.
- It isn't possible to tailor the hardware resources specifically to module needs.
- Changing a library or framework is a daunting task and can't be done often. This results in outdated libraries lying around due to legacy code.
- Hard to find root causes, with many collaborators but no ownership.
As we move through the evolution of software development, things are getting more complex and the drawbacks of the traditional monolith start to outweigh its benefits.