Files
2026-07-02 16:33:17 +03:30

5.4 KiB

Architecture

Negareh API is a NestJS backend built on Fastify, PostgreSQL (via MikroORM), and Redis (caching, OTP, queues). It powers both the customer-facing app and the admin console.

Tech stack

Layer Technology
Runtime Node.js, TypeScript
Framework NestJS 11
HTTP server Fastify (@nestjs/platform-fastify)
ORM MikroORM 6 (PostgreSQL driver)
Auth JWT + refresh tokens, OTP via SMS
Cache / OTP Redis (@keyv/redis, CacheService)
Jobs BullMQ (@nestjs/bullmq)
Real-time Socket.IO (notifications)
File storage AWS S3-compatible bucket
Payments Zarinpal gateway
API docs Swagger at /docs

High-level layout

src/
├── main.ts                 # Bootstrap: Fastify, pipes, interceptors, CORS, Swagger
├── app.module.ts           # Root module — wires all feature modules
├── config/                 # MikroORM, cache, HTTP, Swagger
├── core/                   # Cross-cutting: exceptions, interceptors, middlewares
├── common/                 # Shared entities, decorators, enums, interfaces
├── modules/                # Feature modules (domain boundaries)
└── seeders/                # Database seed data

database/
└── migrations/             # Versioned schema changes (MikroORM migrations)

Feature modules

Each domain lives under src/modules/<name>/ with a consistent internal structure:

modules/<feature>/
├── <feature>.module.ts
├── controllers/            # HTTP layer (thin)
├── providers/              # Business logic (services)
├── repositories/           # Data access (extends EntityRepository)
├── entities/               # MikroORM entities
├── dto/                    # Request/response validation
├── listeners/              # Event handlers (@OnEvent)
├── events/                 # Domain events (EventEmitter2)
└── enum/                   # Feature-specific enums

Current modules:

  • auth — OTP login, JWT, refresh tokens
  • user — End-user accounts and credit
  • admin — Admin accounts
  • roles — Roles and permissions (RBAC)
  • product — Products and categories
  • form-builder — Dynamic product fields and options
  • request — Customer requests / quotes
  • invoice — Invoicing
  • order — Order lifecycle
  • payment — Online payments (Zarinpal)
  • print-form — Print form sections and PDF generation
  • ticket — Support tickets
  • chat — Order/chat messaging
  • notification — SMS, push, WebSocket notifications
  • uploader — File uploads to S3
  • announcements, learnings, criticisms — Content and feedback
  • util — Shared utilities (cache, phone normalization)

Request lifecycle

  1. Fastify receives the HTTP request.
  2. ValidationPipe validates and transforms DTOs (class-validator + class-transformer).
  3. Guards enforce authentication (AuthGuard, AdminAuthGuard) and permissions (@Permissions).
  4. Controller delegates to a service.
  5. Service uses repositories and/or EntityManager for persistence.
  6. ResponseInterceptor wraps successful responses in a standard envelope.
  7. HttpExceptionFilter formats error responses.

Cross-cutting concerns

Response envelope

Successful responses:

{
  "statusCode": 200,
  "success": true,
  "data": { }
}

Paginated responses also include meta:

{
  "statusCode": 200,
  "success": true,
  "data": [],
  "meta": { "total": 100, "page": 1, "limit": 10, "totalPages": 10 }
}

Errors:

{
  "statusCode": 400,
  "success": false,
  "error": { "message": ["..."] }
}

Domain events

Services emit events via EventEmitter2. Listeners in listeners/ handle side effects (notifications, chat, payment follow-up) without bloating the main flow.

Configuration

Use ConfigService for all environment variables. ConfigModule is registered globally in AppModule. Never read process.env directly in application code.

Rate limiting

@nestjs/throttler is configured globally (5 requests per 60 seconds). Sensitive endpoints (OTP, refresh token) use stricter per-route limits via @Throttle or custom decorators.

Design principles

  • Thin controllers, fat services — HTTP concerns stay in controllers; business rules live in services.
  • Reuse before rewrite — Search for existing services, DTOs, and repositories before adding new ones.
  • Module boundaries — Features communicate through exported services or domain events, not by reaching into another module's internals.
  • No duplicated logic — Extend existing modules rather than copying patterns.
  • Transactions for multi-entity writes — Use em.transactional() when several rows must commit or roll back together.

External integrations

Integration Purpose
SMS.ir OTP and transactional SMS
Zarinpal Payment gateway
S3-compatible storage File uploads
Redis OTP cache, general caching, BullMQ

Local development

npm install
npm run start:dev        # Watch mode on APP_PORT (default 4000)

Swagger UI: http://localhost:4000/docs

Database helpers (see database.md):

npm run migration:up     # Apply pending migrations
npm run db:seed          # Run seeders
npm run db:reset         # Drop, create, migrate, seed (destructive)