The Chaos of Horizontal Layers: "Shotgun Surgery"
Most of us start the same way: a Controllers folder, another for Services, and one for Models. In textbooks, it seems like the perfect order, but in the real world, as the application grows, this design becomes a trap.
I bet you've been there: you're asked to change a single field in an Invoice. To do it, you have to open 5 different tabs in your editor: the invoice controller, the invoice service, the repository, the DTO, and the validation logic. All of them are in separate folders, buried among 50 other files that have nothing to do with what you're currently working on.
This is called Shotgun Surgery: a single change forces you to "spray" modifications all over the project. Coupling is high, and the cognitive load—the mental effort required to track where everything is—becomes exhausting.
Vertical Slices: Think in Domains, Not Files
The proposal is simple yet transformative: Divide your application by what it does, not by what it is. Instead of grouping all the world's controllers in one place, we group everything related to a business context or "Slice." Imagine each feature is a cake: instead of just eating the top layer (the frosting), you cut a vertical slice that includes every layer (UI, Logic, Data).
Suggested Structure:
- Auth: (Controller, Service, JWT logic, Repository)
- Users: (Controller, Profile Service, Validations)
- Invoices: (Controller, Tax Logic, PDF Generator, Repo)
By organizing your monolith this way, you are creating decoupled contexts. If tomorrow the Invoices module grows so much that it needs its own database, migrating to a microservice will be almost a "copy-paste" job.
Why is this approach a game-changer?
Dividing by vertical slices isn't just an aesthetic preference; it’s a strategic decision with tangible benefits:
- Ultra-fast Onboarding: When a new developer joins the team and needs to work on "Payments," they don't need to learn the entire system architecture. They just open the
Paymentsfolder, and all the context is right there. - Ease of Testing: Unit and integration tests live right next to the code they verify. It’s much easier to mock dependencies when domain boundaries are crystal clear.
- Time-to-Market Speed: By reducing the friction of navigating through infinite folders, the team can develop features end-to-end much more efficiently.
- Fewer Git Conflicts: Developers are less likely to touch the same files if one is working on
Authand another is working onInvoices. Everyone works in their own "slice."
Pros and Cons: Keeping it Real
Like any architectural decision, this isn't a silver bullet. There are trade-offs you should be aware of.
The Upside (Pros)
- Low Coupling: A change in the tax logic inside
Invoiceshas zero chance of breaking the login process inAuth. - Intuitive Navigation: The project explains itself through its business folders.
- Evolutionary Scalability: It allows you to grow in an organized way. If the monolith becomes a giant, it's already "pre-segmented" to transition into microservices without the pain.
When is it NOT beneficial? (Cons)
- Basic CRUD Applications: If your app only performs 4 basic operations on 2 tables, this structure is Overengineering. You're adding unnecessary folders and files for something that won't need to scale.
- Intentional Code Duplication: Sometimes, to keep slices independent, you might end up duplicating a
UserDTOclass in two different modules. For some purist developers, this is hard to swallow (even though it's often better than a tight dependency). - Team Discipline: It requires everyone to understand where one domain ends and another begins. Without a shared vision, the team might end up creating "Monster Slices" that mix everything up again.
Conclusion
Organizing your code by domains from Day 1 isn't "premature scaling." It's technical hygiene and respect for your future self. A well-modularized monolith is much more valuable, cheaper to maintain, and easier to evolve than a poorly managed microservices ecosystem.
Stop thinking in files; start thinking in business capabilities. Your code (and your mental health) will thank you.