The Biggest Folder Structure Mistake on .NET
If you organize your project folder structure like this, you are making a huge mistake.
/CommandHandlers/AddOrderCommandHandler.cs
/CommandHandlers/AddProductCommandHandler.cs
/CommandHandlers/CancelOrderCommandHandler.cs
/Repositories/ProductsRepository.cs
/Repositories/OrderRepository.cs
/Commands/AddProductCommand.cs
/Commands/AddOrderCommand.cs
/Commands/CancelOrderCommand.cs
/Validators/AddOrderCommandValidator.cs
/Validators/AddProductCommandValidator.cs
It's a simple mistake that can cause a lot of frustration, especially to the new joiners. The good news is that you can go from this to this with a simple technique.
Don't organize like a Librarian. Organize by action/utility.
Let's take a look.
📚 The Librarian Approach
It's common to see projects organized around technical concerns.
We build buckets like Repositories, Services, Validators, and Mappers and group everything according to that.
The problem is that it becomes hard to find anything. Until you are familiar with a code base, it's hard to know where a feature-related code lands. So, the cognitive load required to perform increments, refactoring's, or navigate the code, is high.
Grouping that way, objects naturally will have a vast scope. For example, a Repository or a Service may be the home of code related to multiple features.
To this, I call a Librarian approach to structure code. When we organize it by category as if code is a book.
The MVC Problem
Some templates naturally lead to technological segregation, which reinforces this problem. Yes, MVC, I'm talking about you.
When we create a new MVC API, we get a Controllers, Models, and Views folder. MVC invites us to organize by technology once again.
🎯 The Solution
It's easy to find a book in a Library using the category system. It's even better that we can easily find related books together.
But do we need the same approach in source code? No.
A single feature will have code across files with several responsibilities (categories). When we access code for maintainability, we do it in the context of a Feature. That's the most common access pattern. So, how should we organize it instead? The rule is simple.
Don't organize like a Librarian. Organize by action/utility.
What does that mean?
- We should think about how the code will be used, accessed, and maintained.
- We should think about what code will evolve together.
- We should think about how to reduce the cognitive load of future actions.
This is the Feature Folders concept in a nutshell. These will lead you to Screaming Architecture from Clean Architecture.
So, the structure that we have seen at the beginning of this post can be refactored into:
/CancelOrder/ICancelOrderRepository.cs
/CancelOrder/CancelOrderCommand.cs
/CancelOrder/CancelOrderCommandHandler.cs
/AddOrder/AddOrderCommandHandler.cs
/AddOrder/IAddOrderRepository.cs
/AddOrder/AddOrderCommand.cs
/AddOrder/AddOrderCommandValidator.cs
/AddProduct/AddProductCommand.cs
/AddProduct/AddProductCommandHandler.cs
/AddProduct/AddProductCommandValidator.cs
What about MVC?
If you want to organize your API differently, you have several options:
- You can adapt Minimal APIs (my preferred way).
- You can use API Endpoints (see here).
- You can use Feature Folders (see here).
🏗 The Structure Besides the Project
It doesn't mean you should keep adapters to the outside world inside the same folder and project.
Adapters need to be swappable. You can find an in-depth explanation here.
If you want to see me performing the refactoring, take a look at this video 👇
Let me know what you think about it. Follow me on Twitter (@gsferreira), and let's keep in touch.
Just keep things Simple 🌱