Home/Blog/software engineering
Back to all articles
software engineering#Node.js Architecture#Backend Engineering#Clean Architecture

Scalable Node.js Backend Architecture: Clean Architecture, Clustering & Concurrency

RB
Rajput BhavinArchitect
Founder, Rajput Bhavin Engineering
24 August 2026•11 min read

Quick Answer: Scalable Node.js Architecture#

⚡ Quick Answer / Executive Summary
Building a scalable Node.js backend requires a strict Clean Architecture (3-Tier Layering) separating Controllers, Domain Services, and Data Repositories. To achieve high concurrency, developers must prevent event loop blocking, utilize multi-core clustering or worker threads for CPU-bound tasks, implement connection pooling with PostgreSQL/Redis, and enforce dependency injection for testability and zero technical debt.

The Event Loop Trap: Why Node.js Backends Slow Down#

Node.js is celebrated for its non-blocking, event-driven I/O model. It excels at handling tens of thousands of concurrent network connections (I/O-bound workloads).

However, unarchitected Node.js backends frequently suffer from catastrophic bottlenecks:

  1. ✓Event Loop Starvation: Executing synchronous CPU-heavy operations (like regex parsing, complex crypto hashing, or large JSON stringification) on the main thread, freezing all other concurrent HTTP requests.
  2. ✓Controller-Service Pollution: Writing SQL queries, business calculations, and HTTP header parsing inside a single massive controller file.
  3. ✓Database Connection Exhaustion: Opening a new database connection on every incoming request rather than using a pooled client.
  4. ✓Memory Leaks in Closures: Retaining references in global arrays or event emitters, leading to out-of-memory (OOM) process crashes under heavy load.

3-Tier Clean Architecture Pattern#

At Rajput Bhavin Engineering, we structure Node.js backends with strict separation of concerns:

Architecture Blueprint
System Flow
┌─────────────────────────────────────────────────────────────┐
│                   Layer 1: Transport / API                  │
│       Express / Fastify Routes  •  Zod Request Validation   │
└─────────────────────────────┬─────────────────────────────┘
                              │ Validated DTO
┌─────────────────────────────▼─────────────────────────────┐
│                   Layer 2: Domain Services                  │
│   Business Logic  •  Billing Rules  •  Event Publishing     │
└─────────────────────────────┬─────────────────────────────┘
                              │ Repository Interface
┌─────────────────────────────▼─────────────────────────────┐
│                 Layer 3: Data Access / Persistence          │
│      PostgreSQL (Prisma / Drizzle)  •  Redis Cache Layers   │
└─────────────────────────────────────────────────────────────┘

Production Implementation: Typed Clean Architecture in TypeScript#

#1. Data Transfer Object & Zod Validation (src/modules/orders/order.dto.ts)

TypeScript
Production Code
import { z } from 'zod';

export const CreateOrderSchema = z.object({
  customerId: z.string().uuid(),
  items: z.array(
    z.object({
      productId: z.string().uuid(),
      quantity: z.number().int().positive(),
      price: z.number().positive(),
    })
  ).min(1),
});

export type CreateOrderDTO = z.infer<typeof CreateOrderSchema>;

#2. Domain Service with Dependency Injection (src/modules/orders/order.service.ts)

TypeScript
Production Code
export interface IOrderRepository {
  createOrder(data: CreateOrderDTO, total: number): Promise<string>;
  reserveInventory(items: CreateOrderDTO['items']): Promise<boolean>;
}

export class OrderService {
  constructor(private orderRepo: IOrderRepository) {}

  async processNewOrder(dto: CreateOrderDTO) {
    // 1. Calculate business totals
    const total = dto.items.reduce((sum, item) => sum + item.price * item.quantity, 0);

    // 2. Reserve stock in transactional repository
    const reserved = await this.orderRepo.reserveInventory(dto.items);
    if (!reserved) {
      throw new Error('Insufficient inventory to fulfill order.');
    }

    // 3. Persist order
    const orderId = await this.orderRepo.createOrder(dto, total);
    return { orderId, total, status: 'CONFIRMED' };
  }
}

Scaling Node.js Across Multi-Core CPU Processors#

Because Node.js runs on a single thread by default, a server with 16 CPU cores will only utilize 1 core unless clustering or containerization is implemented:

TypeScript
Production Code
import cluster from 'node:cluster';
import os from 'node:os';
import process from 'node:process';

const numCPUs = os.cpus().length;

if (cluster.isPrimary) {
  console.log(`[Primary ${process.pid}] Forking ${numCPUs} worker processes...`);

  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.warn(`[Worker ${worker.process.pid}] died. Restarting worker...`);
    cluster.fork();
  });
} else {
  // Worker process bootstrap
  import('./server.js');
  console.log(`[Worker ${process.pid}] listening for requests.`);
}

4 Best Practices for Enterprise Node.js Systems#

  1. ✓Use Worker Threads for CPU Tasks: Offload heavy computations (image resizing, PDF generation, cryptographic math) to node:worker_threads so the event loop never lags.
  2. ✓Implement PostgreSQL Connection Pooling: Configure pool size (max: 20, idleTimeoutMillis: 30000) to prevent database server exhaustion.
  3. ✓Structured JSON Logging: Use pino or winston for high-throughput JSON logging with correlation IDs (req.id) for distributed tracing.
  4. ✓Graceful Shutdown Handlers: Listen for SIGTERM and SIGINT signals to close active database connections and finish inflight HTTP requests cleanly before terminating.

Engineer High-Throughput Backends with Rajput Bhavin Engineering#

Build fast, resilient, and maintainable Node.js architectures with systems architect Rajput Bhavin.

Software Engineering Consultation

Need help architecting your modern software stack?

Rajput Bhavin Engineering provides hands-on systems architecture, codebase reviews, and custom cloud software development for technology teams in India and worldwide.

RB

Rajput Bhavin

Systems Architect

Founder and Systems Architect at Rajput Bhavin Engineering in Gota, Ahmedabad. Specializes in high-concurrency cloud software, Next.js web applications, multi-tenant SaaS architecture, and autonomous AI systems.

Connect Now