vicky@dev-blog:~/vickychhetri.com$ cat posts/the-clean-arch-trap-real-talk.md
Let’s be real for a second. We’ve all been there. You start a new Go project, and you want it to be perfect. You spend three days setting up folders: /domain, /usecase, /repository, /delivery. You’re feeling like a 10x architect.
Then, you realize you need to add one single column to your users table.
Suddenly, you’re sweating because you have to update:
- The Postgres schema.
- The
Userstruct in the repository. - The
Userentity in the domain. - The
UserResponseDTO. - The
Mapperfunction that translates between them.
Congratulations, you’ve trapped yourself. 🤡
1. Interfaces are not “Pokémon” (You don’t need to catch ’em all)
Clean Architecture tells you to “program to an interface.” In Java? Sure. In Go? It’s often a mess.
If you only have one database (and let’s be honest, you’re probably not swapping Postgres for MongoDB tomorrow), why are you hiding it behind a UserRepository interface? It just makes “Go to Definition” in VS Code stop working.
The Fix: Start with concrete structs. If you actually need to mock it for a test later, that is when you extract the interface. Go’s implicit interfaces mean you don’t have to plan for it on Day 1.
2. The “Mapping” Tax
On your blog, we talk about large datasets. If you’re fetching 10,000 rows and you map every single one from a DBModel to a DomainModel, you’re just lighting your CPU on fire for fun.
Go is fast because it’s efficient with memory. When you create unnecessary copies of data just to keep your layers “clean,” you’re making the Garbage Collector work overtime.
Pro Tip: If your domain model and your DB model look 99% the same, just use one. It’s okay. The “Clean Architecture Police” aren’t going to break down your door.
3. “Where is the SQL?!”
The worst part of a “clean” backend is trying to debug a slow query. You look at the handler, which calls a service, which calls a use-case, which calls a repository, which finally runs the SQL.
By the time you find the query, you’ve forgotten what you were looking for. When dealing with billions of records, performance is a feature. You need to see your indexes, your joins, and your WHERE clauses clearly—not buried under five layers of abstraction.
💡 The “MY” Way (Keep it Simple)
Instead of building a skyscraper for a lemonade stand, try this:
- Group by Feature: Put your
billinglogic in one folder and youruserslogic in another. Everything (API, Logic, DB) stays together. - The 2-Layer Rule: Most apps only need a Handler (for HTTP/gRPC) and a Store (for DB logic). If a piece of logic gets really complex, then move it into a
logic.gofile. - Performance > Purity: If you need to write a raw SQL query to handle a massive dataset efficiently, do it. Don’t let a design pattern tell you how to write code.
The Bottom Line
Clean Architecture was written in 2012 for enterprise Java apps. It’s 2026, and we’re building high-performance Go backends.
Write code that is easy to delete, not code that is easy to extend. Your future self (and your server’s RAM) will thank you.
vicky@dev-blog:~/vickychhetri.com$ # Mic drop. 🎤
vicky@dev-blog:~/vickychhetri.com$ git push origin master --force # Just kidding, don't force push.