28 Jan 20265 min readNestJS · Architecture · Prisma
NestJS modules before the mess shows up
A growing API doesn’t become a monolith overnight — it becomes one the week you “just put it in AppService for now.”
NestJS makes it easy to move fast and accidentally build a monolith.
Everything starts friendly in AppModule. Then one more method lands in AppService “just for now.” Six months later auth, payments, and messaging are sharing files like roommates who never agreed on chores.
Split by domain while it’s still cheap
I split early: auth, consultations, payments, wallets, messages. Controllers stay thin. Services own the workflow. Adapters talk to the outside world.
A global /api/v1 prefix keeps the product surface clear, while health checks and docs live outside that prefix so ops and product APIs don’t collide. The goal isn’t ceremony — it’s knowing where a change belongs.
Infrastructure is not a junk drawer
Database, cache, OTP, Razorpay, S3, Socket.IO — those belong in infrastructure modules. Feature code should depend on adapters, not raw SDKs sprinkled through services.
That’s how you swap an OTP provider or scale websockets with Redis without rewriting the product. When infrastructure leaks into domain services, every vendor change becomes a rewrite.
Split the data model before it fights you
One giant Prisma schema gets painful fast. Domain-split schemas — identity, auth, consultations, financial, reference — keep ownership obvious. Identity stays near auth. Wallets stay near payments. Reference data stays shared and boring.
Generate OpenAPI early. A documented contract is how frontend teams move without guessing, and how you notice breaking changes before production does. Under a contract deadline, clarity beats cleverness. Every time.
The best module boundary is the one that lets a tired engineer find the right file on the first try.
Further reading
Keep reading