In the world of software development, the architecture of an application plays a crucial role in its long-term success.
While there has been a rising popularity of microservices architecture in recent years, there are still valid reasons to consider building a modular monolith as the starting point for your project.
The “standard” monolith?
This is the one that's often compared in microservices vs. monolith articles, where the entire application is built as a cohesive unit without a clear separation of concerns. They typically lack architectural layers and domain boundaries, resulting in complex codebases that can be difficult to maintain and scale.
Challenges with a standard monolith?
Complexity: Without a clear separation of concerns, the codebase becomes complex and tangled. As the application grows, understanding and maintaining the system becomes increasingly challenging. Modular monoliths are flat structures, so it can be hard to maintain when a monolith has multiple domains (domain boundaries) as the logic is often coupled with other functionalities.
Scalability constraints: Monoliths can face scalability limitations as the entire application needs to be scaled together. This can result in inefficient resource utilisation and difficulty handling high traffic loads.
Limited technological diversity: Monoliths often adopt a single technology stack for the entire application. This can limit the ability to use specialised technologies or frameworks that might be better suited for specific functionalities.
What is a modular monolith?
A modular monolith is an architectural approach that divides the application's domain into smaller, more manageable components or modules. It encourages organising the codebase into logical and structural directories, separating concerns and clarifying boundaries between system functionalities.
Each module within the monolith can have its responsibilities and dependencies, making it easier to reason about, test, and maintain.
While the term "monolith" might typically carry negative connotations due to its association with a single, monolithic codebase, the modular monolith approach introduces a higher level of modularity within the system as they are built as a single, cohesive unit without clear separation of concerns!
Modular monoliths provide the benefits of well-defined modules while retaining the simplicity and ease of deployment typically associated with monolithic architectures.
Why should you build a modular monolith first?
Improved code organisation and maintainability: Modular monoliths promote dividing the application's domain into smaller, well-defined modules. This approach enhances code organisation and maintainability by clearly separating concerns.
Developers can quickly locate and reason about specific functionalities, making it simpler to understand, modify, and extend the system.
Incremental transition to microservices: Starting with a modular monolith provides an incremental path towards a microservices architecture if and when it becomes necessary. The well-defined modules within the monolith can be extracted as separate microservices without a complete system rewrite. This allows for a smoother transition, reducing the risks and complexities of migrating to a distributed architecture.
Easier testing and debugging: Individual modules can be tested and debugged in isolation with modular monoliths, simplifying the testing process. Developers can focus on verifying the behaviour of specific modules without navigating the entire codebase. This granularity enhances the efficiency and effectiveness of testing, leading to higher-quality software.
Reduced complexity compared to microservices: While microservices offer benefits such as scalability and technology diversity, they also introduce additional complexities, such as inter-service communication, service discovery, dependencies between domains/microservices, debugging, and distributed system management. By starting with a modular monolith, you can avoid the upfront complexity of microservices and focus on building a solid foundation. This approach allows you to balance a monolithic architecture's scalability and simplicity.
Deployment simplicity: Modular monoliths retain the simplicity of deploying a single application instead of managing and deploying multiple microservices. Changes and updates can be deployed more efficiently, as only the relevant module or component needs to be updated. This reduces deployment risks, downtime, and version synchronisations on microservices, providing a smoother and more streamlined deployment process.
Independent modules: The best case for independent modules is when you can design their own dependencies injection. This means that the module is fully separated from the main application, which can [help deployments] - this is another topic we could discuss. Kamil Grzybek has some great insights into the benefits of using modular monoliths, and some tremendous in-depth diagrams are worth a read.
Scalability: A modular monolithic architecture can be the basis for your application's growth. As always, this depends on the use case of the project and the business. This is worth exploring deeply when considering architecture; I will discuss it in the summary.
Modular monoliths can provide the foundation for your application to grow!
When considering a modular monolith or microservices, evaluating your application's anticipated growth and scalability needs is crucial. If your system is expected to experience rapid growth, handle a significant number of users, or require dynamic scalability, a microservices architecture may be a more suitable choice from the start, depending on the project's needs.
It might seem controversial, but microservices and monolithic architecture don’t need to battle; choosing the right architecture depends on business requirements. Both methods are helpful and can be a powerful architectural choice when used together!
With a proof of concept, modular monoliths can be an excellent way to start projects quickly as you sidestep the complex aspects of things like planning DevOps workflows, microservices communications, versioning strategies, resource management, and other related considerations!
Starting with a modular monolith puts the core functionality and development of the application from day one.
Modular monoliths can be a gateway to microservices.
The best part is modular monoliths don’t need to stay modular.
With some know-how, you can design your modular architecture to enable easier extraction as separate microservices when scaling and using a distributed architecture! It entirely depends on the business aims and what happens during project scoping, so modular monoliths can be a gateway to microservices, providing a solid foundation for your application and allowing for gradual transitions to microservices in the future.
Any questions? Get in touch on socials or use the form below; we'd be happy to chat. Stay tuned to our blog for weekly stuff on architecture.
If you’re in the mood for more monolithic stuff, here are some gems of knowledge to bookmark:
- Understanding the modular monolith and its ideal use cases, Tech Target.
- Modular Architecture in ASP.NET Core – Building Better Monoliths, Code with Mukesh!
- Microservices Killer: Modular Monolithic Architecture, Medium.
- Monolithic Architecture: What, Why and When, Medium.
- Modular Monolith Architecture, Awesome Architecture.