How We Design Production-Ready NestJS Backends
The NestJS shape we keep using for production APIs: modules as boundaries, boring auth, explicit events, and observability you can run at 2am.
· Updated · 2 min read · Software Engineering
NestJS is easy to start and easy to turn into a decorated junk drawer. Production-ready, for us, means a backend an engineer who did not write it can still operate.
That requirement comes from the work, not from a style guide. In-Out Concepts is a construction platform with event-sourced bones and zero-downtime delivery. Meaco unified fragmented internal workflows. Those systems cannot depend on “the person who knows.”
Here is the NestJS shape we keep returning to.
Modules are bounded contexts, not folders
A vehicles module that imports half the company is not a module. It is a namespace.
We split by change frequency and ownership:
- identity / tenancy
- the core domain (bookings, sites, sessions)
- integrations (payments, storage, WhatsApp)
- admin / reporting (read models are allowed to be separate)
Controllers stay thin. Domain logic does not live in DTOs. If a use-case is a paragraph, it is a service method with a name a human would say out loud.
Auth is a gate, not a decoration
@Roles('admin') sprinkled after the fact is how you get a leak.
We:
- resolve the tenant and the actor first
- pass a
RequestContext, not a pile of optional IDs - deny by default on admin routes
- test the negative cases
Multi-tenant construction and dealership products made this unglamorous and mandatory.
Events where it counts
Not everything needs to be event-sourced. Inventory of mistakes does.
We use domain events when:
- other modules must react (booking confirmed → notification, invoice, calendar)
- we will be asked “what happened?” six months later
- write contention is real
CRUD for settings. Events for money, safety, and anything that ships a crew or a car.
On In-Out, event-sourcing was not a fashion. Sites cannot rewind a week of WhatsApp. The log is the product.
Observability is part of the build
A production Nest app we ship has:
- structured logs with request and tenant IDs
- traces on the slow boundaries (DB, HTTP, queues)
- health that checks dependencies, not just
pong - migrations that run as a release step, not as folklore
Zero-downtime is a release habit. It is also a schema habit. Expand/contract beats “take it down on Friday.”
What we skip
- GraphQL by default. Most of these products need a clear command API and a few read models.
- Shared “god” Prisma service used from every controller.
- Microservices on week one. A modular monolith with events will carry you until it won’t — and you will know when it won’t.
For the product side of the same discipline, read booking platforms. For the mobile clients these APIs serve, React Native vs native.