Domain-driven Design (DDD): File Structure
Project Current File Structure
src folder
-config folder
-database folder
-errors folder
-middlewares folder
-models folder
-repositories folder
-routes folder
-services folder
The src folder should contain our application modules. Everything in it impacts directly how our application functions.
We currently separate our application by file types. So files of the service type are stored in the services folder.
The problem with this is: if we had 10 models, we would have around 40–50 services. Having so many files that do not deal with the same module, or in other words, do not deal with the same domain becomes confusing.
Domain
Is the sphere of knowledge that involves that particular file or module. A set of business rules.
So it is not always good to build our project structure around separating files by their type.
A good way to build our project structure is to isolate things according to their domain, by using modules.
So services related to users such as Authenticate User Service, Create Appointment Service, Create User Service, Update User Avatar Service should all belong to the User Domain.
There would be an Appointment Domain that should only take care of business logic related to appointments, be it to create appointments, list appointments, check available appointments or cancel appointments.
DDD Architecture (Domain-driven Design)
Is a methodology that only applies to the back-end.
Scrum is an agile methodology that allows the team to organize and execute their tasks in an agile manner.
But Scrum does not work in the same way for all teams and projects, we implement the ideas and concepts in the way that makes more sense for our context. DDD works in the same way, there are some concepts that only make sense in enterprise applications (very big applications).
Modules
What are the modules (spheres of knowledge) that we have in our application? The user domain and the appointment domain.
We must create a modules folder to accommodate our modules and inside it we create the users and appointments subfolders.
What can we store inside the modules/users folder?
- DTOs
- Entities/Models
- Repositories (including our fake/mock implementations of them for TDD), services
- Providers that are specific to users (a hash provider that hashes the user password)
- Infra that is specific to users (we will see below what the infrastructure layer means).
Shared
Files with logic that is shared between all modules or multiple modules should be saved in the shared folder that is outside of the modules folder.
What can we store inside the shared folder?
- Errors
- Shared database files(connections and migrations)
- Shared routes
- Shared middleware
- Shared providers (storage provider, email provider).
Infrastructure Layer vs Domain
Domain: Is the sphere of knowledge that involves that particular module. A set of business rules. It takes care of how the application should work.
Infrastructure Layer: Is how data that is initially held by in the domain’s entities (memory) is persisted in databases or another persistent store. the infrastructure layer must not “contaminate” the domain model layer.
NOTE: It contains our application’s technical decisions
When creating eBarber a meeting with the barbers or the users will help you better determine the Domain/Business Logic. But a barber does not have the expertise to tell you what tools you should use: if you should use an ORM or a Query Builder.
A CTO and/or senior engineers decide things that are related to the infrastructure layer.
The user domain knows that when a user signs up he should receive an email, but it does not know what tool is being used to send emails. That is the role of the Infrastructure Layer.
Read: Microsoft & DDD-oriented Microservices
Infra
To accommodate the infrastructure layer we can create multiple infra folders. We can have an infra folder inside our shared folder and inside each of our modules (users and appointments).
We can create a database folder inside the infra folder to deal with things related to our database. We should give it a specific name, since our project uses TypeORM, we should name our database folder as typeorm.
We can also create a http folder inside the infra folder to deal with things related to our http requests and responses such as server.ts, routes and middleware.
NOTE: Once again we should give it a specific name. If we were using another type of protocol such as gRPC we would give it another name.
Additional Notes
- Infra will store all information that is responsible for a specific package or library.
Example: TypeORM. - Errors may be shared but they are not part of the infra layer.
- It is good to include things that could change in the infra folder.
Example 1: Express routes and middleware.
Example 2: Imagine if we need to change the communication protocol from HTTP to gRPC.
Project File Structure using DDD
Final considerations
The main advantage of Domain Driven Design is being able to create code with well-defined components that have clear contracts between them. This allows us to better define their responsibilities, makes updating or replacing one of these components much easier with less impact on the overall system.
The key disadvantage is that DDD assumes that you have a fairly clear picture of the solution you are trying to produce, but that is not always the case. A solution to this problem is to create a small prototype, possibly iterate over it multiple times until you have enough understanding to come up with the reasonable design. This can delay the start of the project, but is likely to result in a more maintainable solution.
This post only touches the tip of the iceberg, I encourage you to read more about DDD including about the application layer.