Files
KIS-TOiR/backend/architecture.md
2026-03-21 16:00:27 +03:00

7.2 KiB

Backend Architecture

Backend stack:

  • Node.js
  • TypeScript
  • NestJS
  • Prisma ORM
  • PostgreSQL
  • jose

The backend is generated from the DSL specification.

Each DSL entity becomes:

  • Prisma model
  • NestJS module
  • CRUD controller
  • Service
  • DTO definitions

Project Structure

server/ package.json

prisma/ schema.prisma

src/ main.ts app.module.ts

auth/
  auth.module.ts
  guards/
  decorators/
  interfaces/

config/
  env.validation.ts

modules/

  {entity}/
    {entity}.module.ts
    {entity}.controller.ts
    {entity}.service.ts

    dto/
      create-{entity}.dto.ts
      update-{entity}.dto.ts
      {entity}.response.dto.ts

Module Rules

Each entity generates exactly one NestJS module.

Example:

Entity: Equipment

Module:

modules/ equipment/ equipment.module.ts equipment.controller.ts equipment.service.ts


Controller Rules

Each entity controller must expose these endpoints:

  • GET /{resource} — list
  • GET /{resource}/:pk — get one
  • POST /{resource} — create
  • PATCH /{resource}/:pk — update
  • DELETE /{resource}/:pk — delete

The path parameter :pk must use the primary key attribute name from the DSL, not always :id.

Path parameter by primary key

Entity Primary key (DSL) Path parameter Example routes
Equipment id :id GET /equipment/:id, PATCH /equipment/:id
EquipmentType code :code GET /equipment-types/:code, PATCH /equipment-types/:code
RepairOrder id :id GET /repair-orders/:id, PATCH /repair-orders/:id

Rule: Use the actual primary key name. For example, EquipmentType has PK code, so routes must be:

  • GET /equipment-types/:code
  • PATCH /equipment-types/:code
  • DELETE /equipment-types/:code

Do not use /equipment-types/:id when the entity primary key is code.


Health Endpoint

Every generated backend must expose a health endpoint so that runtime and orchestration can verify the API is up.

  • Path: GET /health
  • Response: JSON with a status indicator (e.g. { "status": "ok" }).

Controller example

@Public()
@Controller("health")
export class HealthController {
  @Get()
  getHealth() {
    return { status: "ok" };
  }
}

Register the health controller in the root app module (or a dedicated health module). No authentication required for this endpoint.


Authentication and Authorization

The generated backend must include explicit auth infrastructure by default.

Generate:

  • AuthModule
  • JWT guard
  • roles guard
  • @Public()
  • @Roles()
  • typed authenticated principal
  • typed env validation for auth/runtime variables

Rules:

  1. /health must remain public.
  2. Generated CRUD routes must be protected by default.
  3. JWT verification must use issuer + audience + JWKS.
  4. Authorization roles must be extracted only from realm_access.roles.
  5. The generated backend must not use deprecated Keycloak-specific Node adapters.

CRUD RBAC defaults

Apply these defaults to generated CRUD controllers:

  • GET -> viewer, editor, admin
  • POST -> editor, admin
  • PATCH -> editor, admin
  • PUT -> editor, admin
  • DELETE -> admin

These defaults must be encoded in generated guards/decorators, not left as informal guidance.


List Endpoint

List endpoint must support pagination and filters via query parameters.

Example:

GET /equipment?page=0&size=10

Response format must follow React Admin requirements:

{ "data": [], "total": number }


Service Layer

Services implement CRUD operations using Prisma.

Example:

findAll() findOne(id) create(data) update(id, data) remove(id)


DTO Rules

Create DTO:

  • contains required fields
  • does NOT contain generated primary keys

Update DTO:

  • all fields optional

Response DTO:

  • mirrors domain entity attributes

Naming Conventions

Entity naming

DSL entities use PascalCase. Generated backend artifacts use the same base name in lowercase for folders and file prefixes.

  • Equipment → equipment module
  • EquipmentType → equipment-type module (or equipment-type as path segment)
  • RepairOrder → repair-order module

Module naming

One entity = one module folder. Folder name = entity name in kebab-case (lowercase, hyphen-separated).

  • Equipment → modules/equipment/
  • EquipmentType → modules/equipment-type/
  • RepairOrder → modules/repair-order/

Controller naming

  • File: {entity-kebab}.controller.ts
  • Class: EquipmentController, EquipmentTypeController, RepairOrderController

Examples:

  • equipment.controller.ts
  • equipment-type.controller.ts
  • repair-order.controller.ts

Service naming

  • File: {entity-kebab}.service.ts
  • Class: EquipmentService, EquipmentTypeService, RepairOrderService

Examples:

  • equipment.service.ts
  • equipment-type.service.ts
  • repair-order.service.ts

DTO naming

  • Create: create-{entity-kebab}.dto.ts (e.g. create-equipment.dto.ts, create-repair-order.dto.ts)
  • Update: update-{entity-kebab}.dto.ts (e.g. update-equipment.dto.ts)
  • Response: {entity-kebab}.response.dto.ts or use entity name for list/detail response types

Resource Naming Rules

API resource paths are derived from the entity name:

  1. PascalCase → kebab-case: Replace camelCase with lowercase hyphenated segments.
  2. Pluralize: Use plural form for the resource path (list endpoint represents a collection).
Entity (DSL) API path (resource)
Equipment /equipment
EquipmentType /equipment-types
RepairOrder /repair-orders

Rules:

  • Equipmentequipment (already singular-looking; path is still /equipment for consistency with REST resource naming).
  • EquipmentTypeequipment-types (camelCase "EquipmentType" → "equipment-type", then plural → "equipment-types").
  • RepairOrderrepair-orders ("RepairOrder" → "repair-order" → "repair-orders").

Standard endpoints per resource:

  • GET /{resource} — list
  • GET /{resource}/:pk — get one (pk = primary key name, e.g. :id or :code)
  • POST /{resource} — create
  • PATCH /{resource}/:pk — update
  • DELETE /{resource}/:pk — delete

See Controller Rules above for the rule that :pk must match the entity's primary key attribute name.


Environment and runtime

  • Environment variables: Backend requires runtime and auth variables. See backend/runtime-rules.md.
  • .env: Generated project must include a .env (and .env.example) with database, auth, and CORS variables so the app starts without runtime errors.
  • PrismaService: Must follow backend/prisma-service.md (OnModuleInit, $connect; no beforeExit).
  • Prisma client: Add "postinstall": "prisma generate" (or equivalent) to package.json so the client is generated after install.
  • Migrations: Document or run npx prisma migrate dev after schema generation. See backend/runtime-rules.md and generation/backend-generation.md.