Modular Design is very important in any large scale application. It helps with decoupling application domains, enables developers to focus only on their specific domains and makes the on-boarding process a lot easier. Also, if you decide to split your monolith into microservices, it will be less painful.
The basic idea behind modular design is to organize your project files based on their purpose instead of their type.
One of the most common problems you'll encounter during the process of modularizing your app, is the trap of circular-dependency between your modules. Circular dependencies are often an indicator of the need to extract a third module or merge the two.
But how to organize them?
Layered Modules
I've personally encountered the problem of circular dependencies in EVERY project that I have worked on.
One time, as I was diagnosing the structure of one of my apps to find the origin of circular dependencies between its modules, I realized that the modules can function independently only up to a certain layer.
In this concrete example, the problem was the API layer (the app was a back-end service). I needed to aggregate information from multiple modules to finally accomplish the task and output the results to the front-end developers.
So I decided to change the project structure from this:
To this:
And after the refactoring process was done, there was no circular dependencies between my modules anymore. Also, the module hierarchy was much more easier to understand.
As you can see, This structure is a combination of layered architecture and modular design. You can personalize this to fit your own needs.
How do I organize my modules now?
Currently, I categorize my modules into 3 main categories:
/src
/db-modules
/feature-modules
/api-modules
DB Modules
- They can manage and validate the database models related to their domains independently
feature Modules
- They can manage and validate the database models related to their domains independently
- They implement logic and perform business tasks independently
- They can import from DB Modules
API Modules
- They can manage and validate the database models related to their domains independently
- They implement logic and perform business tasks independently
- They implement the API interface and handle API requests independently (without relying on other API modules)
- They can import from DB Modules
- They can import from Feature Modules
Basically, the extent to which a module can handle its own concerns, determines the type of the module. Also you can introduce more groups as the complexity grows (e.g: you can add an aggregator module group to integrate multiple API modules which is useful for dealing with microservices and external APIs).
What do you think about this architecture? leave a comment down below
Top comments (0)