164 lines
5.4 KiB
Markdown
164 lines
5.4 KiB
Markdown
# 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:
|
|
|
|
```json
|
|
{
|
|
"statusCode": 200,
|
|
"success": true,
|
|
"data": { }
|
|
}
|
|
```
|
|
|
|
Paginated responses also include `meta`:
|
|
|
|
```json
|
|
{
|
|
"statusCode": 200,
|
|
"success": true,
|
|
"data": [],
|
|
"meta": { "total": 100, "page": 1, "limit": 10, "totalPages": 10 }
|
|
}
|
|
```
|
|
|
|
Errors:
|
|
|
|
```json
|
|
{
|
|
"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
|
|
|
|
```bash
|
|
npm install
|
|
npm run start:dev # Watch mode on APP_PORT (default 4000)
|
|
```
|
|
|
|
Swagger UI: `http://localhost:4000/docs`
|
|
|
|
Database helpers (see [database.md](./database.md)):
|
|
|
|
```bash
|
|
npm run migration:up # Apply pending migrations
|
|
npm run db:seed # Run seeders
|
|
npm run db:reset # Drop, create, migrate, seed (destructive)
|
|
```
|