The Broad Way

[ Sharp Mind · Sharp Blade · Sharp Spirit ]

root@construct:~
/clean-architecture-after-252-use-cases
$_
<-- back to /logs
2025-12-20//LOG

Clean Architecture After 252 Use Cases

I've been practicing Clean Architecture for years. TOPO Contabil has 252 use cases following the pattern strictly. Here's what I've actually learned, not the theory from the book, but the reality of living with it at scale. WHAT WORKS Testability. This is the undeniable win. Every use case is a pure function of its dependencies. Inject mocks, assert outputs. Our unit test suite runs in 18 seconds because nothing touches a database or network. When a test fails, you know EXACTLY what broke because the scope is tiny. One use case, one responsibility, one test file. Onboarding. New developers are productive in days, not weeks. The structure is so predictable that once you've seen one module, you've seen them all. "Where does the business logic live?" In the use case. "Where does the database logic live?" In the repository. "Where does the validation live?" In the DTO. Always. No exceptions. Refactoring confidence. When every dependency is injected through an interface, swapping implementations is trivial. We migrated from one tax calculation provider to another by writing a new adapter. Zero changes to business logic. Zero changes to tests. The use cases didn't know or care. Domain modeling. Forcing yourself to separate domain entities from database entities makes you THINK about your domain. Our fiscal document entity has behavior. It can validate itself, calculate totals, transition states. It's not a bag of properties that gets mutated from the outside. This matters when your domain is complex. WHAT DOESN'T WORK The ceremony. Creating a new feature means creating a minimum of 5 files. Use case, input DTO, output DTO, controller method, maybe a new repository method. For a simple CRUD endpoint this is overkill. We've accepted this tax because the benefits at scale outweigh the cost, but I won't pretend it doesn't feel heavy sometimes. Mapper hell. Converting between domain entities, database entities, DTOs, and response objects means writing a LOT of mapping code. It's tedious and error-prone. We've automated some of it but there's still a mapper layer that exists purely because of architectural purity. Over-abstraction temptation. When you're in the Clean Architecture mindset, you start abstracting EVERYTHING. Do you really need a repository interface for a lookup table that will never change providers? Probably not. But the pattern says you should, so you do, and now you have an interface, an implementation, and a module registration for something that could have been a direct database call. WHEN TO BREAK THE RULES After 252 use cases, I've developed a sense for when the architecture should flex: Simple reads that just fetch and return data don't need a full use case. A thin query service that goes straight to the repository is fine. Not everything needs a command/query split. Prototype features get a simpler structure. When we're not sure a feature will survive, we build it with less ceremony. If it proves valuable, we refactor into full Clean Architecture. If not, it's easy to delete. Performance-critical paths sometimes need to break the layers. Our report generation module bypasses the repository abstraction and uses raw SQL because the ORM was too slow. The architecture purist in me cries, but the pragmatist knows a 30-second report is worse than an impure layer. Clean Architecture is not a religion. It's a tool. Use it where it helps. Relax it where it doesn't. The goal is maintainable software, not architectural purity for its own sake. 252 use cases later, I'd do it all again. But I'd be less dogmatic about it from the start.
The Broad Way | Kinho.dev