Standards for authoring SDD plugin agents — frontmatter, self-containment, skill references, and no-user-interaction rules.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
CMDO architecture standards for Node.js/TypeScript backends with strict layer separation.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.
CMDO ("Commando") architecture for Node.js/TypeScript backends with strict separation between infrastructure and domain concerns.
Operator → Controller → Model Use Cases
↓ ↓ ↑
Config → [All layers] → Dependencies (injected by Controller)
↓
DAL
| 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 | 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 |
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)// 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);
});
Provides raw I/O capabilities and orchestrates application lifecycle. NO domain knowledge - only infrastructure.
What Operator Does:
What Operator Does NOT Do:
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:
stop()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
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:
dotenv.config() inside loadConfig() (not at module level)process.env access is ONLY allowed in src/config/process.env in Operator, Controller, Model, or DAL layersWhat it does NOT contain: Business logic, database queries.
Structure:
src/config/
├── index.ts # loadConfig() function, Config type
└── validation.ts # Optional: validation helpers
Request/response handling, combines I/O + config for domain-specific operations, creates Dependencies object for Model.
Controller's Key Role:
httpClient + config.paymentUrl → chargePayment)HTTP Handlers: Each file in http_handlers/ corresponds to an API namespace (e.g., /users, /orders) and exports a router. The create_controller.ts imports these routers and wires them together with the Dependencies object for Model use-cases.
Handler Naming: Use operationId from OpenAPI spec with handle prefix (e.g., createUser → handleCreateUser).
Health Check Endpoints: Lifecycle probes (/health, /readiness) are handled by Operator on a separate port, NOT in the Controller.
What it does NOT contain: Database queries, business logic (delegates to Model).
Structure:
src/controller/
├── http_handlers/ # One file per API namespace
│ ├── users.ts # Exports usersRouter
│ ├── orders.ts # Exports ordersRouter
│ └── index.ts # Re-exports all routers
├── create_controller.ts # Assembles routers, creates Dependencies for Model
└── index.ts # Exports only
Business logic via definitions + use-cases. Model never imports from outside its module.
Definitions Rules:
type or interface)Use Case Rules:
// ✅ GOOD: Discriminated union results
type CreateUserResult =
| { readonly success: true; readonly user: User }
| { readonly success: false; readonly error: 'email_exists' | 'invalid_email' };
const createUser = async (
deps: Dependencies,
args: CreateUserArgs
): Promise<CreateUserResult> => {
// Business logic using only injected dependencies
};
What it does NOT contain: HTTP handling, direct database queries, external imports.
Structure:
src/model/
├── definitions/ # TypeScript types ONLY (no Zod/validation)
│ ├── user.ts
│ └── index.ts
├── use-cases/ # One function per file
│ ├── create_user.ts
│ ├── update_user.ts
│ └── index.ts
├── dependencies.ts # Dependencies interface
└── index.ts # Exports only
Data access functions that directly handle database queries. No repository pattern - use simple, focused functions instead.
DAL Rules:
find_user_by_id.ts)index.ts re-exports all functionsWhat it does NOT contain: Business logic, HTTP handling, repository abstractions.
Structure:
src/dal/
├── find_user_by_id.ts
├── insert_user.ts
├── update_user.ts
└── index.ts # Re-exports all functions
All observability follows OpenTelemetry standards. Telemetry is initialized by the Operator layer.
Use Pino with OpenTelemetry context injection.
Log Levels:
| Level | When to use |
|---|---|
debug | Detailed debugging (disabled in production) |
info | Normal operations, state changes, requests |
warn | Recoverable issues, deprecations |
error | Failures requiring attention |
When to Use Info Logging:
Log before and after every domain action or permanent state change:
Required Log Fields:
| Field | Required | Description |
|---|---|---|
level | Yes | Log level (debug, info, warn, error) |
time | Yes | ISO 8601 timestamp |
component | Yes | Source component (operator, controller, dal) |
msg | Yes | Human-readable message |
traceId | Auto | OpenTelemetry trace ID |
spanId | Auto | OpenTelemetry span ID |
userId | Context | User ID if authenticated |
requestId | Context | Request correlation ID |
error | On error | Error object with stack |
Logging by Layer: Loggers are created by the Operator layer and passed down. Each layer creates a child logger with its component name.
Security Rule: Never log sensitive data (passwords, tokens, credit cards, PII beyond IDs).
Required Metrics:
| Metric | Type | Labels | Layer |
|---|---|---|---|
http.server.request.duration | Histogram | method, route, status | Operator |
http.server.request.count | Counter | method, route, status | Operator |
db.client.operation.duration | Histogram | operation, table | DAL |
db.client.connection.pool.usage | UpDownCounter | state (active/idle) | DAL |
business.operation.count | Counter | operation, result | Model |
Metric Naming: Follow OpenTelemetry semantic conventions:
. as separator (e.g., http.server.request.duration)http, db, business)duration, count, size)Wrap business operations with spans using @opentelemetry/api.
Span Attributes: Use OpenTelemetry semantic conventions:
| Attribute | Type | Example |
|---|---|---|
http.method | string | GET, POST |
http.route | string | /api/users/:id |
http.status_code | int | 200, 404 |
db.system | string | postgresql |
db.operation | string | SELECT, INSERT |
db.statement | string | SQL query (sanitized) |
user.id | string | User identifier |
withTraceContext() for all logsCRITICAL: Use lowercase_with_underscores for ALL filenames
✅ create_user.ts, find_user_by_id.ts, user_repository.ts
❌ createUser.ts (camelCase), CreateUser.ts (PascalCase), create-user.ts (kebab-case)
Inside a module, nothing should ever import from its own index.ts. All imports within a module must use relative paths. The barrel is the module's public API for external consumers only. For nested modules, the same barrel rules apply.
// Given this structure:
// controllers/
// ├── index.ts ← barrel: re-exports all routers
// ├── users/
// │ ├── index.ts
// │ └── users_router.ts
// └── health/
// ├── index.ts
// └── health_router.ts
// In controllers/users/users_router.ts:
// GOOD: relative path to sibling sub-module barrel
import { healthCheck } from '../health';
// BAD: importing from own module's barrel
import { healthCheck } from '@/controllers';
import { healthCheck } from '@/controllers/health';
When implementing a new feature, follow this order to minimize rework and ensure clean architecture:
Start in components/contract/:
openapi.yaml - request/response schemascontract generate-types)This ensures types flow from contract → server → frontend.
Start in src/model/:
definitions/use-cases/Model is pure business logic with no external dependencies.
In src/dal/:
In src/controller/:
http_handlers/ fileIn src/operator/:
Only modify if the feature requires:
| Step | Location | What to Do |
|---|---|---|
| 1 | contract/openapi.yaml | Define endpoint, request/response schemas |
| 2 | model/definitions/ | Add feature-specific types |
| 3 | model/use-cases/ | Implement business logic |
| 4 | model/dependencies.ts | Extend Dependencies if needed |
| 5 | dal/ | Add database functions |
| 6 | controller/http_handlers/ | Add HTTP handler |
| 7 | controller/create_controller.ts | Wire Dependencies |
| 8 | Tests | Test each layer independently |
Server components require configuration from components/config/. The backend-scaffolding skill generates the initial server structure including a minimal config schema with port, host, and logLevel fields.
Before committing backend code, verify:
src/index.ts) only imports and starts operatorprocess.envlowercase_with_underscoresThis skill defines no input parameters or structured output.
아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.
로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.
아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.
Standards for authoring SDD plugin agents — frontmatter, self-containment, skill references, and no-user-interaction rules.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Scaffolds Node.js/TypeScript backend components with CMDO architecture, driven by component settings.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Intent-to-command mappings for fullstack-typescript tech pack features.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Create change specification and implementation plan with dynamic phase generation. Supports feature, bugfix, refactor, and epic types.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Orchestrates the full change lifecycle — routes actions to phase-specific sub-files.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Scaffolds CI/CD pipeline definitions using GitHub Actions for build, test, lint, and deploy workflows.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.