Skip to content

Architecture Overview

Projects generated by spoonfeed follow a simplified 3-layer architecture designed for clarity and maintainability.

Layered Architecture

graph TB
    subgraph presentation ["PRESENTATION LAYER"]
        direction LR
        p1["Controllers"] ~~~ p2["DTOs"]
        p3["Interceptors"] ~~~ p4["Guards / Pipes"]
    end

    subgraph business ["BUSINESS LAYER"]
        direction LR
        b1["Services"] ~~~ b2["Domain Logic"]
        b3["Mappers"] ~~~ b4["Validators"]
    end

    subgraph data ["DATA ACCESS LAYER"]
        direction LR
        d1["Repositories"] ~~~ d2["Entities"]
        d3["External APIs"] ~~~ d4["Queue Clients"]
    end

    presentation --> business
    business --> data

    style presentation fill:#dbeafe,color:#1e293b,stroke:#3b82f6
    style business fill:#e2e8f0,color:#1e293b,stroke:#475569
    style data fill:#f1f5f9,color:#1e293b,stroke:#94a3b8

Generator Pipeline

The scaffolding process follows a linear pipeline from user input to a fully generated project:

flowchart LR
    A[CLI Prompts] --> B[Config Validation]
    B --> C[Template Engine]
    C --> D[Package Merger]
    D --> E[Env Merger]
    E --> F[AI Context]
    F --> G[Manifest]

    style A fill:#dbeafe,stroke:#3b82f6,color:#1e293b
    style G fill:#dcfce7,stroke:#22c55e,color:#1e293b

Template Layers

Templates are composed from three layers. The generator merges them into the final output project:

flowchart TB
    subgraph Templates
        direction TB
        base[Base Templates]
        types[Project-Type Overrides]
        recipes[Recipe Templates]
    end

    base --> gen[Generator]
    types --> gen
    recipes --> gen
    gen --> out[Output Project]

    style base fill:#e2e8f0,stroke:#475569,color:#1e293b
    style types fill:#e2e8f0,stroke:#475569,color:#1e293b
    style recipes fill:#e2e8f0,stroke:#475569,color:#1e293b
    style gen fill:#dbeafe,stroke:#3b82f6,color:#1e293b
    style out fill:#dcfce7,stroke:#22c55e,color:#1e293b

Key Principles

  • Single Responsibility — Each class should have one reason to change
  • Dependency Inversion — Depend on abstractions, not concretions
  • Maximum 3 Dependencies — A class should inject no more than 3 dependencies (excluding Logger and ConfigService). If exceeded, refactor into smaller services.
  • Persist-First Pattern — Write to database before forwarding queue messages
  • Fail Fast — Validate input as early as possible

Directory Structure

src/
  main.ts                        # Application bootstrap
  app.module.ts                  # Root module
  config/                        # Configuration
    configuration.ts             # Config factory
    *.config.ts                  # Typed config interfaces
  shared/                        # Shared utilities (non-feature)
    constants/                   # Application constants
    decorators/                  # Custom decorators
    filters/                     # Exception filters
    guards/                      # Auth guards
    interceptors/                # Response interceptors
    interfaces/                  # Shared interfaces
    pipes/                       # Custom pipes
    utils/                       # Utility functions
  infrastructure/                # External integrations
    database/                    # Database setup
      entities/                  # ORM entities
      migrations/                # Database migrations
      repositories/              # Custom repositories
    http/                        # External HTTP APIs
      <api-name>/
        <api>.module.ts
        <api>.service.ts
        dto/                     # External API DTOs
        interfaces/
    queue/                       # Message queue
    storage/                     # File/S3 storage
    notifications/               # Slack, email, etc.
  app/
    modules/                     # Feature modules
      <module-name>/
        <module>.module.ts
        <module>.controller.ts
        <module>.service.ts
        dto/
          request/               # Incoming request DTOs
          response/              # Outgoing response DTOs
        interfaces/              # Module-specific interfaces
        mappers/                 # Data transformation

Structure Rules

Pattern When to Use
Feature modules in app/modules/ Business feature code
Infrastructure in infrastructure/ External integrations (DB, APIs, queues)
Shared utilities in shared/ Cross-cutting concerns
No index.ts barrel files Prevents circular dependencies

Test Structure

Tests live in a separate tests/ directory at the project root, mirroring the src/ layout:

tests/
  unit/                     # Unit tests (*.spec.ts)
  integration/              # Integration tests (*.integration.spec.ts)
  e2e/                      # End-to-end tests (*.e2e-spec.ts)
  factories/                # Shared test data factories
  helpers/                  # Shared test utilities
  • Unit tests — One file per source file. Mock only external dependencies, never code you own.
  • Integration tests — Use real NestJS testing modules with Testcontainers for databases and services.
  • E2E tests — HTTP boundary tests against a running application.
  • Factories — Shared test data builders used across all suites.

Path Alias

The @/* alias resolves to src/* in all generated projects:

// Use this
import { UserService } from '@/users/user.service';

// Not this
import { UserService } from '../../users/user.service';