Job Level API - Dokumentasi Teknis
Overview
Dokumentasi teknis untuk developer yang akan maintain atau develop lebih lanjut pada Job Level API module.
Arsitektur
Job Level module menggunakan pola Command & Query:
job-level/
├── commands/
│ ├── create.cmd.ts
│ ├── update.cmd.ts
│ └── soft-delete.cmd.ts
├── queries/
│ ├── get-job-level.query.ts
│ └── get-job-level-by-id.query.ts
├── DTO/
├── helper/ # generateSlug, getOrderIndex, mapToDetailResponse
├── validation/
├── job-level.controller.ts
├── job-level.service.ts
├── job-level.module.ts
└── docs/
Tech Stack
- Framework: NestJS
- Database: PostgreSQL dengan Prisma ORM
- Validation: class-validator, class-transformer
- Auth: AuthGuard (JWT), tenant dari current user
- RLS: createTenantClient
Database Schema
Table: job_levels
| Column | Type | Keterangan |
|---|---|---|
| id | UUID | PK |
| tenant_id | UUID | NOT NULL |
| key | VARCHAR(50) | UNIQUE, slug dari name |
| name | VARCHAR(100) | NOT NULL |
| description | VARCHAR(255) | NULL |
| order_index | INT | NOT NULL, untuk urutan |
| is_active | BOOLEAN | DEFAULT true |
| created_by | UUID | NULL |
| created_by_name | VARCHAR(100) | NULL |
| created_at | TIMESTAMPTZ | DEFAULT now() |
| updated_by | UUID | NULL |
| updated_by_name | VARCHAR(100) | NULL |
| updated_at | TIMESTAMPTZ | @updatedAt |
| deleted_by | UUID | NULL |
| deleted_by_name | VARCHAR(100) | NULL |
| deleted_at | TIMESTAMPTZ | NULL |
Relasi:
JobGrade.level_id→JobLevel.idJobTitle.level_id→JobLevel.id
Module Structure
1. Commands
CreateJobLevelCommand
- File:
commands/create.cmd.ts - Alur:
- createTenantClient(tenantId)
- key = JobLevelHelper.generateSlug(dto.name)
JobLevelValidation.validateExistence(client, { key })— key harus belum dipakai (throw jika ada)- orderIndex = JobLevelHelper.getOrderIndex(client) — max(order_index) + 1
- Transaction:
jobLevel.create({ tenantId, key, name, description, orderIndex, isActive: true }) - mapToDetailResponse(result)
UpdateJobLevelCommand
- File:
commands/update.cmd.ts - Alur: validateExistence(
{ id }) — Catatan: Implementasi menggunakan validateExistence; untuk update seharusnya validateNotFound (record harus ada). Key di-regenerate dari dto.name. → update
SoftDeleteJobLevelCommand
- File:
commands/soft-delete.cmd.ts
2. Helper
- generateSlug(str) — Lowercase, non-alphanumeric →
-, trim - getOrderIndex(client) — max(orderIndex) + 1 (dari record yang tidak soft-deleted)
- mapToDetailResponse(jl) — Map ke response DTO
3. Validation
JobLevelValidation
- validateExistence(client, where) — Throw 400
job-level.is_existjika record ada (dipakai untuk validasi key unik pada create) - validateNotFound(client, where) — Throw 404
job-level.is_not_foundjika record tidak ada
Controller Endpoints
- Base route:
job-levels - POST
/— create - GET
/— findAll - GET
/:id— findOne - PATCH
/:id— update - DELETE
/:id— softDelete (remove)
Error Codes
job-level.is_not_found— Record tidak ditemukanjob-level.is_exist— Key sudah dipakai (create)