Microservices are a scaling tool, not a default architecture. We split NestJS apps when teams, deploy cadence, or failure domains genuinely diverge — not because a diagram looks cleaner.
Redis-backed Bull queues handle async work that should never block HTTP responses: email, webhooks, report generation. Each worker gets its own container with constrained memory so a poison job cannot take down the API tier.
Shared contracts live in a versioned package — DTOs, event schemas, and OpenAPI stubs — so services do not drift silently across repos.
Our Docker Compose layout separates API, workers, and Redis with explicit health checks. Deploys stay boring — and that is the point.
When incidents happen, bounded contexts with clear ownership shorten mean time to resolution more than any orchestration dashboard.
