Skip to content
Development
Skill

/backend-standards

CMDO architecture standards for Node.js/TypeScript backends with strict layer separation.

From plugin
sdd
4459 skills7 agents3 commands
Install
$ npx -y skills add LiorCohen/sdd --skill backend-standards --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/backend-standards

Context preview

The summary Claude sees to decide when to auto-load this skill.

CMDO architecture standards for Node.js/TypeScript backends with strict layer separation.

SKILL.md

backend-standards.SKILL.md
name: backend-standards
description: CMDO architecture standards for Node.js/TypeScript backends with strict layer separation.

Backend Standards Skill

CMDO ("Commando") architecture for Node.js/TypeScript backends with strict separation between infrastructure and domain concerns.

---

Architecture: CMDO (Controller Model DAL Operator)

Operator → Controller → Model Use Cases
   ↓            ↓              ↑
Config → [All layers] → Dependencies (injected by Controller)
                               ↓
                             DAL

Key Distinction: Infrastructure vs Domain

| Layer | Knows About | Example | |-------|-------------|---------| | **Operator** | Infrastructure only | "Here's a DB connection and generic HTTP client" | | **Config** | URLs and settings | `paymentGatewayUrl: "https://api.stripe.com"` | | **Controller** | Domain concerns | "Use httpClient + paymentGatewayUrl to charge a card" | | **Model** | Business logic only | "Calculate total, then call chargePayment()" |

**Key Principle:** Operator provides raw I/O capabilities (generic HTTP client, cache client, DB connection) with NO domain knowledge. Controller combines I/O + config to create domain-specific operations.

---

Layer Overview

| Layer | Path | Responsibility | |-------|------|----------------| | **Entry Point** | `src/index.ts` | Bootstrap only (ONLY file with side effects) | | **Operator** | `src/operator/` | Raw I/O capabilities (DB, HTTP clients, cache), lifecycle management, state machine | | **Config** | `src/config/` | Environment parsing, validation, type-safe config | | **Controller** | `src/controller/` | Request/response handling, combines I/O + config for domain calls, creates Dependencies for Model | | **Model** | `src/model/` | Business logic (definitions + use-cases), receives Dependencies | | **DAL** | `src/dal/` | Data access, queries, mapping DB ↔ domain objects |

---

Entry Point: src/index.ts

**Rules:**

  • `src/index.ts` is the ONLY file that runs code on import (exception to the "index.ts exports only" rule for application entry points)
  • All other files export functions/types with NO side effects when imported
  • NO logic beyond importing and starting the operator
  • NO configuration loading, validation, or setup logic
// src/index.ts - Entry point (exception to index.ts rule for entry points)
import { createOperator } from "./operator";
import { loadConfig } from "./config";

const main = async (): Promise<void> => {
    const config = loadConfig();
    const operator = createOperator({ config });
    await operator.start();
};

main().catch((error) => {
    console.error("Failed to start operator:", error);
    process.exit(1);
});

---

Layer 1: Operator

Provides raw I/O capabilities and orchestrates application lifecycle. **NO domain knowledge** - only infrastructure.

**What Operator Does:**

  • Creates database connection pool (raw I/O capability)
  • Creates generic HTTP client (for external calls - if needed)
  • Creates cache client (if needed)
  • Binds DAL functions with database client
  • Creates Controller, passing raw I/O + DAL + config
  • Manages lifecycle via state machine (IDLE → STARTING → RUNNING → STOPPING → STOPPED)
  • Manages HTTP server for API endpoints
  • Manages lifecycle probes on separate port (health/readiness for Kubernetes)
  • Handles Unix signals for graceful shutdown (SIGTERM, SIGINT, SIGHUP)
  • Initializes telemetry (logger, metrics) before other modules

**What Operator Does NOT Do:**

  • NO domain-specific calls (e.g., "call payment gateway", "fetch user from auth service")
  • NO business logic
  • NO knowledge of what the raw I/O will be used for

**Lifecycle State Machine:**

IDLE → STARTING:PROBES → STARTING:DATABASE → STARTING:HTTP_SERVER → RUNNING
                                                                        ↓
STOPPED ← STOPPING:PROBES ← STOPPING:DATABASE ← STOPPING:HTTP_SERVER ←─┘

**Unix Signal Handling:**

| Signal | Source | Action | |--------|--------|--------| | `SIGTERM` | Kubernetes pod termination, `kill` command | Graceful shutdown | | `SIGINT` | Ctrl+C from terminal | Graceful shutdown | | `SIGHUP` | Terminal hangup | Graceful shutdown |

When a signal is received: 1. Log the signal with info level 2. Initiate graceful shutdown via `stop()` 3. Wait for all connections to drain 4. Exit with code 0 (success) or 1 (error)

**Structure:**

src/operator/
├── create_operator.ts      # Main factory, state machine, lifecycle
├── create_database.ts      # Database connection pool
├── create_http_server.ts   # HTTP server wrapper
├── lifecycle_probes.ts     # Health/readiness endpoints (separate port)
├── state_machine.ts        # Generic state machine implementation
├── logger.ts               # Pino logger with OpenTelemetry
├── metrics.ts              # OpenTelemetry metrics
└── index.ts                # Exports only

---

Layer 2: Config

Environment parsing, validation, type-safe config objects.

**CRITICAL: Use dotenv for ALL environment variable access.** Direct `process.env` access is FORBIDDEN outside the Config layer.

**Environment Variable Rules:** 1. **dotenv is mandatory**: Always use `dotenv.config()` inside `loadConfig()` (not at module level) 2. **Config layer ONLY**: `process.env` access is ONLY allowed in src/config/ 3. **Type-safe access**: All other layers receive typed Config object 4. **Validation required**: Validate required vars and throw if missing 5. **Default values**: Provide sensible defaults for optional vars 6. **NO direct access elsewhere**: NEVER use `process.env` in Operator, Controller, Model, or DAL layers

**What it does NOT contain:** Business logic, database queries.

**Structure:**

src/config/
├── index.ts                # loadConfig() function, Config type
└── validation.ts           # Optional: validation helpers

---

Layer 3: Controller

Request/response handling, combines I/O + config for domain-specific op

Read more
Ships withsdd

Structure for AI-assisted development AI coding assistants are powerful but chaotic. You prompt, you get code, but then what?

Get the whole plugin

Other skills on sdd.