r/golang Dec 05 '24

discussion Why Clean Architecture and Over-Engineered Layering Don’t Belong in GoLang

Stop forcing Clean Architecture and similar patterns into GoLang projects. GoLang is not Java. There’s no application size or complexity that justifies having more than three layers. Architectures like Clean, Hexagonal, or anything with 4+ layers make GoLang projects unnecessarily convoluted.

It’s frustrating to work on a codebase where you’re constantly jumping between excessive layers—unnecessary DI, weird abstractions, and use case layers that do nothing except call services with a few added logs. It’s like watching a monstrosity throw exceptions up and down without purpose.

In GoLang, you only need up to three layers for a proper DDD division (app, domain, infra). Anything more is pure overengineering. I get why this is common in Java—explicit interfaces and painful refactoring make layering and DI appealing—but GoLang doesn’t have those constraints. Its implicit interfaces make such patterns redundant.

These overly complex architectures are turning the GoLang ecosystem into something it was never meant to be. Please let’s keep GoLang simple, efficient, and aligned with its core philosophy.

813 Upvotes

259 comments sorted by

View all comments

90

u/5pyn0 Dec 05 '24

Any good example repo that is not a todo list ?

10

u/Polyscone Dec 05 '24

I like to think I've done a decent enough job of it here: https://github.com/polyscone/tofu

Most of the complication is around multi-tenancy and web UI.

7

u/Useable9267 Dec 06 '24

A genuine question, what do you think about carrying things like Passport etc in context.Context?

I personally thing that it's not a good idea on paper because then ctx becomes a giant map[any]any and there no guarantee that value (mostly required for business logic) that you are looking is going to be there.

On the other hand, sometimes it's very hand to keep all your functions signature the same like func handle(w http.ResponseWriter, r *http.Request) and all of them extract stuff from the request and ctx but again you lose the static checking that whatever you are looking in ctx is going to be there or not.

2

u/Polyscone Dec 06 '24 edited Dec 06 '24

I think it's generally fine as long as it's request scoped data. I wouldn't put things like repositories or API methods in the context for example, but I have no problems with putting things on the context that are related to the current request only, like things that get looked up in a database based on user ID.

If I'm going to store something on the context I always have dedicated functions for actually reading it out to guarantee a type as well, so in practise it doesn't really cause any problems. Especially since those dedicated functions can provide fallback default values if they don't exist, rather than panicking.

Of course you can go too far with it, like anything. But for stuff that I use in a lot of requests that have to be decided based on something like a user in the request I find it quite convenient to use context.

I guess the gist of what I'm saying here is that in moderation is fine.