# TalentScale - SaaS Psikotest API 🚀

Selamat datang di repository *backend* **TalentScale**, sebuah platform SaaS Psikotest. Proyek ini menggunakan **Golang** dengan arsitektur **Clean Architecture + Domain-Driven Design (Feature-Based/Modular Monolith)**. 

Struktur ini adalah *best practice* untuk aplikasi skala besar (SaaS) di tahun 2026. Desain ini memastikan *codebase* kita mudah di-*maintain*, mudah di-*test*, dan siap dipecah menjadi *microservices* jika sewaktu-waktu dibutuhkan.

---

## 📂 Struktur Direktori Utama

```text
TalentScale/
├── cmd/
│   └── api/
│       └── main.go                 # Entry point aplikasi. Inisialisasi dependency injection ada di sini.
├── internal/                       # Folder sakral. Kode di sini TIDAK BISA di-import oleh project Go lain di luar modul ini.
│   ├── config/                     # Konfigurasi aplikasi (Struct dari env / config file)
│   ├── domain/                     # Core Business: Interfaces dan Struct Entity (TIDAK BOLEH import package dari folder lain)
│   ├── features/                   # Modul/Domain fitur aplikasi
│   │   ├── assessment/             # Contoh modul: Assessment (Soal, dll)
│   │   │   ├── handler/            # Layer presentasi (HTTP/REST, gRPC)
│   │   │   ├── usecase/            # Layer logika bisnis
│   │   │   └── repository/         # Layer database & API eksternal
│   │   ├── candidate/              # Contoh modul: Peserta Tes
│   │   └── tenant/                 # Contoh modul: Data Perusahaan Klien (SaaS)
│   └── middleware/                 # HTTP/RPC middlewares (Auth, Rate Limiter)
├── pkg/                            # Kode library yang BOLEH di-import secara bebas (seperti package open source)
│   ├── database/                   # Wrapper koneksi database
│   └── logger/                     # Wrapper logging
├── deployments/                    # Dockerfile, manifest Kubernetes, docker-compose.yml
└── scripts/                        # Bash/Makefile scripts (Migrasi DB, script deploy)
```

---

## 📐 Aturan & Panduan Berlapis (The Clean Rules)

Agar *codebase* kita konsisten bertahun-tahun ke depan, ikuti aturan ketat dari lapisan terdalam hingga terluar ini:

### 1. `domain/` (Inti Aplikasi)
- **Tugas**: Berisi pendefinisian entitas bisnis (struct) dan kontrak *interface* untuk *Usecase* dan *Repository*.
- **Aturan Konsistensi**:
  - **TIDAK BOLEH** import *package* internal apa pun (tidak boleh import `usecase`, `repository`, dsb).
  - Hanya boleh menggunakan standar library Go (`time`, `context`, dll).
  - Letakkan DTO (*Data Transfer Object* / Struct untuk Request-Response) di sini jika di-*share* antar layer.

### 2. `features/{nama-fitur}/repository/` (Akses Data)
- **Tugas**: Berinteraksi dengan *database* (Postgres, Mongo, Redis) atau *external API* (misal: Payment Gateway).
- **Aturan Konsistensi**:
  - Wajib *implement* *interface* yang ada di `domain`.
  - Layer ini *hanya* peduli bagaimana cara simpan/ambil data, **TIDAK BOLEH** ada logika bisnis ("Jika A, maka B").
  - Biasakan menaruh `context.Context` sebagai parameter pertama (sangat berguna untuk *timeout* dan fitur SaaS berbasis `TenantID`).

### 3. `features/{nama-fitur}/usecase/` (Logika Bisnis)
- **Tugas**: Orkestrasi alur aplikasi. Tempat di mana semua *business rules* berada.
- **Aturan Konsistensi**:
  - Wajib *implement* *interface* Usecase dari `domain`.
  - Berinteraksi dengan *database* HANYA melalui *interface* *Repository* (jangan pernah pakai koneksi `db` secara langsung di sini).
  - Jika ada *error* bisnis (misal: "Saldo tidak cukup"), kembalikan *error* yang jelas dari sini.

### 4. `features/{nama-fitur}/handler/` (Delivery/Presentasi)
- **Tugas**: Menangkap *request* dari luar (HTTP JSON, XML, gRPC) dan meneruskannya ke *Usecase*.
- **Aturan Konsistensi**:
  - **DILARANG KERAS** memanggil *Repository* secara langsung. Wajib lewat *Usecase*.
  - Tugasnya hanya 3:
    1. Parse *Request* (dari JSON ke Struct).
    2. Panggil *Usecase*.
    3. Format *Response* (dari Struct/Error ke JSON).
  - Logic if-else bisnis tidak boleh ada di sini.

---

## 🛠 Panduan Langkah demi Langkah: Membuat Fitur Baru

Jika Anda ingin membuat fitur baru (misalnya **`Result`** atau Hasil Tes Psikotest), ikuti urutan ini:

**Langkah 1: Definisikan Domain (`internal/domain/result.go`)**
1. Buat struct `Result` yang merepresentasikan tabel di database.
2. Buat `interface ResultRepository` yang berisi method CRUD.
3. Buat `interface ResultUsecase` yang berisi method alur bisnis.

**Langkah 2: Buat Repository (`internal/features/result/repository/postgres_repository.go`)**
1. Buat struct yang menerima koneksi *database*.
2. Implementasikan semua fungsi yang ada di `interface ResultRepository`.

**Langkah 3: Buat Usecase (`internal/features/result/usecase/result_usecase.go`)**
1. Buat struct yang membutuhkan *dependency* `domain.ResultRepository`.
2. Tulis implementasi logika bisnisnya di sini.
3. Gunakan *Repository* via interface untuk simulasi (*mocking*) nanti saat *Unit Testing*.

**Langkah 4: Buat Handler (`internal/features/result/handler/http_handler.go`)**
1. Buat struct *handler* yang menerima `domain.ResultUsecase`.
2. Bikin fungsi-fungsi seperti `CreateResult(w http.ResponseWriter, r *http.Request)`.
3. Validasi JSON input, panggil *Usecase*, lalu berikan respons JSON balikan.

**Langkah 5: Daftarkan (Wiring) di `cmd/api/main.go`**
1. Inject Repo: `resultRepo := repository.NewPostgresResultRepository(db)`
2. Inject Usecase: `resultUsecase := usecase.NewResultUsecase(resultRepo)`
3. Inject Handler: `resultHandler := handler.NewHTTPHandler(resultUsecase)`
4. Daftarkan di Router: `mux.HandleFunc("POST /results", resultHandler.CreateResult)`

---

## 🎯 Best Practices Tambahan untuk SaaS
1. **Multi-Tenancy**: Karena ini aplikasi SaaS, usahakan hampir setiap method di *Usecase* dan *Repository* menerima `tenant_id` dari *Context* agar data satu klien tidak pernah bocor ke klien lain.
2. **Context Cancellation**: Selalu gunakan `context.Context` dan sebarkan dari `Handler -> Usecase -> Repo`. Jika *user* membatalkan HTTP *request* (tutup *browser*), eksekusi DB akan otomatis berhenti.
3. **Dependency Injection**: Jangan memakai `init()` function atau Global Variables untuk menyimpan koneksi Database. Gunakan Dependency Injection secara eksplisit di `main.go`.

Happy Coding! Jaga struktur ini agar aplikasi tetap sehat hingga berjuta-juta baris kode.
