How do you structure and maintain large Go modular monoliths without drowning in architecture ?
I’m working on an e-commerce backend in Go. Stack is mostly:
- Postgres
- sqlc
- huma + chi
- SuperTokens self-hosted
- ERPNext as the single source of truth, with the backend mostly acting as a BFF/gateway
- Stripe
I started with good intentions: keep things clean, modular, properly separated, and follow some DDD/hexagonal architecture ideas without going full enterprise Java mode.
But once the project grows, keeping everything “clean” becomes exhausting.
A lot of the time, I feel like I’m spending more energy maintaining the architecture than actually shipping features.
The things that constantly become painful are:
- Wiring dependencies
- Avoiding circular imports
- Transaction management across modules
- Figuring out where code should live
- Keeping boundaries clean without creating tons of boilerplate
- Deciding when an interface is actually useful versus just architecture cosplay
At smaller scale, Go feels amazing: simple, productive, and fast.
At larger scale, though, I sometimes feel like there’s a missing middle ground between:
- “Just put everything in handlers/services”
- Ultra-pure clean architecture with 500 layers and endless interfaces
For people maintaining large Go codebases:
- What architectural style ended up working for you?
- Any package layout patterns that scale well?
- How do you structure modules or bounded contexts?
- Do you use mediators, event buses, or domain events?
- How much coupling do you tolerate in practice?
- Any repos, talks, articles, or open-source examples you’d recommend?
I’d really appreciate practical advice from people who’ve gone through this already.
>Edit: Formatting for readability.