How to better structure your next Node.js project? The Modular Monolith Approach.
Embrace the simplicity and scalability of the Modular Monolith Architecture.
In the past, I’ve worked on various projects both with Node.js and other technologies.
I’ve seen many project structures that last and such that need to be changed, refactored, or even rewritten from zero.
It’s crucial to have a well-structured project from the beginning.
It helps with the developer experience (DE), scalability and maintainability of the application, testing, and much more.
After reading this article, you will learn about:
technical responsibility separation
domain responsibility separation
technical vs. domain responsibility
modular monolith architecture
a better way to structure your next Node.js project
The Old Way of a Node.js Project Structure (MVC)
Maybe the most popular way of project structure is the so-called MVC pattern.
MVC stays for Model-View-Controller.
As the name says, you structure your application into 3 main folders - `models`, `controllers`, and `views`.
Here you structure and separate your folders by their technical responsibility.
// ⛔ MVC (technical responsibility)
/src
└───models
│ │ user.js
| | paymentProfile.js
│ │ ...
└───controllers
│ │ user.js
| | paymentProfile.js
| | ...
└───views
│ │ user.js
│ │ ...
└───...
Models’s user.js
is responsible for fetching data from the Database and/or some other ORM/DB stuff.
Controller’s user.js
is responsible for handling HTTP requests, responses, or business logic.
Views’s user.js
is responsible for visualizing responses and data.
The Downsides of MVC and Technical Responsibility Separation
While being one of the most popular ways to structure an app, I haven’t seen the structure to scale well.
After some time, it’s getting hard to navigate through the codebase and find a particular functionality.
Looking into the different models doesn’t tell you anything about their relation and structure.
Controllers invite developers to dump all the logic into controllers is a recipe for a disaster.
With this approach is hard to achieve a good elasticity and maintainability of the system.
I’m tempted to say there’s a better mental model to structure your application.
It’s by structuring your project by domain responsibility.
Domain Responsibility Separation
Another way to look at your application’s structure is by the domain perspective.
Each folder represents a domain that’s part of the business.
For example, user management, product management, etc.
This way, the structure of the applications resembles the way the business operates and its different domains/areas.
It’s more intuitive because it’s closer to the real-world problems we solve as software developers.
💡 An application’s structure should tell you what it does and provide information about its domain.
An application’s structure in the financial and e-commerce sector should be different.
They have different domains and operations, so this should be clear when looking into the codebase.
Start with the Modular Monolith Architecture
In recent years, developers have favored the Microservices Architecture and while it provides benefits like better scalability, independence, etc., it comes with a cost - complexity.
The other way of designing your application is the Modular Monolith Approach.
Better start with a Modular Monolith and while the application is evolving, improve the architecture if needed.
As the Gall’s Law states:
All complex systems that work evolved from simpler systems that worked. If you want to build a complex system that works, build a simpler system first, and then improve it over time.
You can easily migrate from a Modular Monolith Architecture to a Microservices Architecture later because each module/domain is separated and isolated.
For example, each module folder could go into a separate Microservice very easily.
// ✅ Modular Monolith (domain responsibility)
/src
└───account
│ │ userQueries.js
| | userMutations.js
| | userServices.js
| | ...
│ │ index.js
└───billing
│ │ paymentProfileQueries.js
| | paymentProfileMutations.js
| | paymentProfileServices.js
| | ...
│ │ index.js
└───...
By embracing a Modular Monolith structure you get the scalability, isolation, and organization of the microservices structure but without the complexity related to the Distributed Systems.
This way, you can iterate and move faster.
You take the best from both worlds.
⭐ TL;DR
Structuring your project by Technical Responsibility like MVC doesn’t scale well in time.
A better approach is to structure your project by Domain Responsibility where each folder is a separate module and business domain of the application.
Embrace the Modular Monolith Architecture for a simple, robust, scalable, and maintainable project structure.
👋 Let’s connect
You can find me on LinkedIn or Twitter.
I share daily practical tips to level up your skills and become a better engineer.
Thank you for being a great supporter, reader, and for your help in growing to 12.4K+ subscribers this week 🙏
This newsletter is funded by paid subscriptions from readers like yourself.
If you aren’t already, consider becoming a paid subscriber to receive the full experience!
Think of it as buying me a coffee twice a month, with the bonus that you also get all my templates and products for FREE.
You can also hit the like ❤️ button at the bottom to help support me or share this with a friend to get referral rewards. It helps me a lot! 🙏
Thanks for the shoutout, Petar
Awesome article Ivan. I like the "screaming architecture" pattern which lines up more with how you suggested.
Thanks as well for the shout-out on the Meta Hiring Manager article 🙏