Menyelesaikan object user dan init base
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
APP_ENV=development #development / test / prod
|
||||||
|
PORT=3069
|
||||||
|
DATABASE_URL=postgresql://USERNAME:PASSWORD@HOST:5432/DATABASE?schema=public
|
||||||
|
|
||||||
|
JWT_SECRET=GANTI_DENGAN_SECRET
|
||||||
|
JWT_REFRESH_SECRET=GANTI_DENGAN_REFRESH_SECRET
|
||||||
|
|
||||||
|
SWAGGER_ENABLED=true
|
||||||
|
|
||||||
|
LOG_LEVEL=debug
|
||||||
@@ -1 +1,4 @@
|
|||||||
node_modules
|
node_modules
|
||||||
|
RayLab-Core.zip
|
||||||
|
RayLab-Core.rar
|
||||||
|
.env
|
||||||
@@ -1,208 +0,0 @@
|
|||||||
# RayLab Core Phase 1 --- Module Design
|
|
||||||
|
|
||||||
## Tujuan
|
|
||||||
|
|
||||||
Fase 1 mendefinisikan pembagian modul pada RayLab Core. Setiap modul
|
|
||||||
memiliki tanggung jawab yang jelas (Single Responsibility Principle),
|
|
||||||
mudah dikembangkan secara independen, dan berkomunikasi melalui service
|
|
||||||
atau interface yang telah ditentukan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Prinsip Desain
|
|
||||||
|
|
||||||
- Modular dan mudah dikembangkan.
|
|
||||||
- Loose coupling, high cohesion.
|
|
||||||
- Setiap modul memiliki domain bisnis yang jelas.
|
|
||||||
- Antar modul tidak mengakses database modul lain secara langsung.
|
|
||||||
- Komunikasi dilakukan melalui service, interface, atau event jika
|
|
||||||
diperlukan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Daftar Modul
|
|
||||||
|
|
||||||
## 1. Core System
|
|
||||||
|
|
||||||
Tanggung jawab:
|
|
||||||
|
|
||||||
- Bootstrap aplikasi
|
|
||||||
- Dependency Injection
|
|
||||||
- Configuration
|
|
||||||
- Logging
|
|
||||||
- Health Check
|
|
||||||
- Global Middleware
|
|
||||||
- Exception Filter
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## 2. Identity
|
|
||||||
|
|
||||||
Mengelola identitas pengguna.
|
|
||||||
|
|
||||||
Fitur:
|
|
||||||
|
|
||||||
- User
|
|
||||||
- Profile
|
|
||||||
- Session
|
|
||||||
- Authentication (implementasi pada fase berikutnya)
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## 3. Authorization
|
|
||||||
|
|
||||||
Mengelola hak akses.
|
|
||||||
|
|
||||||
Fitur:
|
|
||||||
|
|
||||||
- Group
|
|
||||||
- Role
|
|
||||||
- Permission
|
|
||||||
- Domain Access
|
|
||||||
- Resource Access
|
|
||||||
- Special Access
|
|
||||||
|
|
||||||
Hierarki:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
User
|
|
||||||
↓
|
|
||||||
Group
|
|
||||||
↓
|
|
||||||
Role
|
|
||||||
↓
|
|
||||||
Permission
|
|
||||||
↓
|
|
||||||
Domain Access
|
|
||||||
↓
|
|
||||||
Resource Access
|
|
||||||
↓
|
|
||||||
Special Access
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## 4. Registry
|
|
||||||
|
|
||||||
Pusat registrasi aplikasi dan layanan.
|
|
||||||
|
|
||||||
Mengelola:
|
|
||||||
|
|
||||||
- Application
|
|
||||||
- Service
|
|
||||||
- Domain
|
|
||||||
- Adapter
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## 5. Configuration
|
|
||||||
|
|
||||||
Menyimpan konfigurasi yang dapat diubah saat runtime.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
- Theme
|
|
||||||
- Timezone
|
|
||||||
- Upload Limit
|
|
||||||
- Default Language
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## 6. Dashboard
|
|
||||||
|
|
||||||
Menyediakan data untuk dashboard RayLab.
|
|
||||||
|
|
||||||
Dashboard tidak menyimpan business logic, hanya menggabungkan data dari
|
|
||||||
modul lain.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## 7. Health
|
|
||||||
|
|
||||||
Monitoring internal.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
- Database
|
|
||||||
- Redis
|
|
||||||
- Storage
|
|
||||||
- Adapter
|
|
||||||
- Service Status
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## 8. Adapter
|
|
||||||
|
|
||||||
Lapisan integrasi layanan eksternal.
|
|
||||||
|
|
||||||
Contoh adapter:
|
|
||||||
|
|
||||||
- Nextcloud
|
|
||||||
- Jellyfin
|
|
||||||
- Immich
|
|
||||||
- Minecraft
|
|
||||||
- Future integrations
|
|
||||||
|
|
||||||
Semua komunikasi eksternal harus melalui Adapter.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## 9. Storage
|
|
||||||
|
|
||||||
Mengelola storage logis.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
- Storage
|
|
||||||
- Volume
|
|
||||||
- Quota
|
|
||||||
- Path Mapping
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## 10. Media
|
|
||||||
|
|
||||||
Media Manager berada di RayLab Core.
|
|
||||||
|
|
||||||
Tanggung jawab:
|
|
||||||
|
|
||||||
- Membuat struktur folder otomatis
|
|
||||||
- Mengelola folder private/shared
|
|
||||||
- Memindahkan media saat perubahan visibilitas
|
|
||||||
- Mengubah permission
|
|
||||||
- Memicu refresh library Jellyfin
|
|
||||||
- Audit media
|
|
||||||
|
|
||||||
Jellyfin hanya membaca media, bukan mengelolanya.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## 11. Audit
|
|
||||||
|
|
||||||
Mencatat seluruh aktivitas penting sistem.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
- Login
|
|
||||||
- Perubahan permission
|
|
||||||
- Pembuatan user
|
|
||||||
- Penghapusan group
|
|
||||||
- Perubahan konfigurasi
|
|
||||||
- Aktivitas adapter
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Aturan Antar Modul
|
|
||||||
|
|
||||||
- Tidak ada modul yang mengakses repository modul lain secara
|
|
||||||
langsung.
|
|
||||||
- Semua business logic berada pada modul pemilik domain.
|
|
||||||
- Integrasi dilakukan melalui service atau adapter.
|
|
||||||
- Setiap modul harus dapat diuji secara mandiri.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Hasil Akhir Fase 1
|
|
||||||
|
|
||||||
Setelah fase ini selesai, RayLab Core memiliki struktur modular yang
|
|
||||||
menjadi fondasi untuk seluruh pengembangan berikutnya.
|
|
||||||
@@ -1,218 +0,0 @@
|
|||||||
# RayLab Core Phase 2 --- Domain Model
|
|
||||||
|
|
||||||
## Tujuan
|
|
||||||
|
|
||||||
Fase 2 mendefinisikan domain bisnis yang dimiliki RayLab Core beserta
|
|
||||||
hubungan antar domain. Domain model menjadi dasar implementasi entity,
|
|
||||||
service, API, dan database.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Prinsip Domain
|
|
||||||
|
|
||||||
- Domain mewakili konsep bisnis, bukan tabel database.
|
|
||||||
- Setiap domain memiliki tanggung jawab yang jelas.
|
|
||||||
- Domain saling berinteraksi melalui service, bukan akses langsung ke
|
|
||||||
data domain lain.
|
|
||||||
- Seluruh aturan bisnis berada pada domain terkait.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Domain Utama
|
|
||||||
|
|
||||||
## Identity
|
|
||||||
|
|
||||||
Mengelola identitas pengguna.
|
|
||||||
|
|
||||||
### Entity
|
|
||||||
|
|
||||||
- User
|
|
||||||
- Profile
|
|
||||||
- Session
|
|
||||||
|
|
||||||
### Tanggung Jawab
|
|
||||||
|
|
||||||
- Identitas pengguna
|
|
||||||
- Informasi profil
|
|
||||||
- Status sesi
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Authorization
|
|
||||||
|
|
||||||
Mengelola hak akses sistem.
|
|
||||||
|
|
||||||
### Entity
|
|
||||||
|
|
||||||
- Group
|
|
||||||
- Role
|
|
||||||
- Permission
|
|
||||||
- Domain Access
|
|
||||||
- Resource Access
|
|
||||||
- Special Access
|
|
||||||
|
|
||||||
### Model IAM
|
|
||||||
|
|
||||||
``` text
|
|
||||||
User
|
|
||||||
↓
|
|
||||||
Group
|
|
||||||
↓
|
|
||||||
Role
|
|
||||||
↓
|
|
||||||
Permission
|
|
||||||
↓
|
|
||||||
Domain Access
|
|
||||||
↓
|
|
||||||
Resource Access
|
|
||||||
↓
|
|
||||||
Special Access
|
|
||||||
```
|
|
||||||
|
|
||||||
### Penjelasan
|
|
||||||
|
|
||||||
**Group** adalah identitas organisasi utama.
|
|
||||||
|
|
||||||
**Role** merupakan kumpulan tanggung jawab.
|
|
||||||
|
|
||||||
**Permission** mendefinisikan aksi yang diizinkan.
|
|
||||||
|
|
||||||
**Domain Access** menentukan aplikasi atau domain yang dapat diakses.
|
|
||||||
|
|
||||||
**Resource Access** menentukan objek di dalam aplikasi yang boleh
|
|
||||||
digunakan.
|
|
||||||
|
|
||||||
**Special Access** merupakan override khusus untuk user tertentu tanpa
|
|
||||||
mengubah group atau role.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Registry
|
|
||||||
|
|
||||||
Entity:
|
|
||||||
|
|
||||||
- Application
|
|
||||||
- Service
|
|
||||||
- Domain
|
|
||||||
- Adapter
|
|
||||||
|
|
||||||
Berfungsi sebagai katalog seluruh layanan RayLab.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
Entity:
|
|
||||||
|
|
||||||
- Setting
|
|
||||||
- Setting Category
|
|
||||||
|
|
||||||
Digunakan untuk konfigurasi yang dapat diubah saat runtime.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Storage
|
|
||||||
|
|
||||||
Entity:
|
|
||||||
|
|
||||||
- Storage
|
|
||||||
- Volume
|
|
||||||
- Quota
|
|
||||||
|
|
||||||
Mengelola penyimpanan logis dan alokasi kapasitas.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Media
|
|
||||||
|
|
||||||
Entity:
|
|
||||||
|
|
||||||
- Library
|
|
||||||
- Folder Template
|
|
||||||
- Folder Mapping
|
|
||||||
- Folder Permission
|
|
||||||
|
|
||||||
Media Manager mengendalikan struktur media, sedangkan Jellyfin hanya
|
|
||||||
membaca media yang telah disiapkan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Audit
|
|
||||||
|
|
||||||
Entity:
|
|
||||||
|
|
||||||
- Audit Log
|
|
||||||
- Audit Category
|
|
||||||
- Audit Target
|
|
||||||
|
|
||||||
Mencatat seluruh aktivitas penting sistem.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Health
|
|
||||||
|
|
||||||
Entity:
|
|
||||||
|
|
||||||
- Health Check
|
|
||||||
- Service Status
|
|
||||||
|
|
||||||
Digunakan untuk memantau kesehatan layanan internal.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## System
|
|
||||||
|
|
||||||
Entity:
|
|
||||||
|
|
||||||
- Job
|
|
||||||
- Notification
|
|
||||||
|
|
||||||
Digunakan untuk proses internal seperti background job dan notifikasi.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Relasi Antar Domain
|
|
||||||
|
|
||||||
``` text
|
|
||||||
Identity
|
|
||||||
│
|
|
||||||
└── Authorization
|
|
||||||
|
|
||||||
Authorization
|
|
||||||
│
|
|
||||||
├── Registry
|
|
||||||
├── Media
|
|
||||||
├── Storage
|
|
||||||
└── Dashboard
|
|
||||||
|
|
||||||
Registry
|
|
||||||
│
|
|
||||||
└── Adapter
|
|
||||||
|
|
||||||
Semua Domain
|
|
||||||
│
|
|
||||||
└── Audit
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Aturan Domain
|
|
||||||
|
|
||||||
- Domain hanya memiliki satu tanggung jawab utama.
|
|
||||||
- Tidak ada duplikasi data antar domain.
|
|
||||||
- Setiap domain memiliki service sendiri.
|
|
||||||
- Entity hanya mewakili data dan aturan dasar domain.
|
|
||||||
- Komunikasi lintas domain dilakukan melalui service.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Hasil Akhir Fase 2
|
|
||||||
|
|
||||||
Pada akhir fase ini RayLab Core memiliki model domain yang menjadi acuan
|
|
||||||
untuk:
|
|
||||||
|
|
||||||
- Desain database
|
|
||||||
- Layered Architecture
|
|
||||||
- API
|
|
||||||
- Implementasi modul
|
|
||||||
- Pengembangan fitur di masa depan
|
|
||||||
@@ -1,237 +0,0 @@
|
|||||||
# RayLab Core Phase 3 --- Layered Architecture
|
|
||||||
|
|
||||||
## Tujuan
|
|
||||||
|
|
||||||
Fase 3 menetapkan standar arsitektur kode RayLab Core agar seluruh modul
|
|
||||||
memiliki pola implementasi yang konsisten, mudah dipelihara, mudah
|
|
||||||
diuji, dan mudah dikembangkan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Filosofi
|
|
||||||
|
|
||||||
Setiap request mengikuti alur yang sama:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
HTTP Request
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Controller
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Application Service
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Domain Service
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Repository
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Database
|
|
||||||
```
|
|
||||||
|
|
||||||
Setiap layer hanya boleh berinteraksi dengan layer yang berada tepat di
|
|
||||||
bawahnya.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Layer 1 --- Controller
|
|
||||||
|
|
||||||
Tanggung jawab:
|
|
||||||
|
|
||||||
- Menerima HTTP Request
|
|
||||||
- Validasi dasar
|
|
||||||
- Memanggil Application Service
|
|
||||||
- Mengembalikan HTTP Response
|
|
||||||
|
|
||||||
Tidak boleh:
|
|
||||||
|
|
||||||
- Mengakses database
|
|
||||||
- Menyimpan business logic
|
|
||||||
- Memanggil layanan eksternal secara langsung
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Layer 2 --- Application Service
|
|
||||||
|
|
||||||
Application Service mengimplementasikan use case.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
- Create User
|
|
||||||
- Update User
|
|
||||||
- Delete User
|
|
||||||
- Assign Group
|
|
||||||
- Grant Special Access
|
|
||||||
- Create Domain
|
|
||||||
|
|
||||||
Tanggung jawab:
|
|
||||||
|
|
||||||
- Mengatur alur bisnis
|
|
||||||
- Mengoordinasikan Domain Service, Repository, dan Adapter
|
|
||||||
- Menjalankan transaksi bila diperlukan
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Layer 3 --- Domain Service
|
|
||||||
|
|
||||||
Seluruh aturan bisnis ditempatkan di layer ini.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
- Owner tidak boleh dihapus
|
|
||||||
- Group default tidak boleh dihapus
|
|
||||||
- Validasi Domain Access
|
|
||||||
- Validasi Permission
|
|
||||||
- Validasi Resource Access
|
|
||||||
|
|
||||||
Business rule tidak boleh berada di Controller.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Layer 4 --- Repository
|
|
||||||
|
|
||||||
Repository bertanggung jawab terhadap akses data.
|
|
||||||
|
|
||||||
Fungsi utama:
|
|
||||||
|
|
||||||
- Save
|
|
||||||
- Find
|
|
||||||
- Update
|
|
||||||
- Delete
|
|
||||||
- Query
|
|
||||||
|
|
||||||
Repository tidak mengetahui HTTP maupun business logic.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Layer 5 --- Database
|
|
||||||
|
|
||||||
Menggunakan PostgreSQL dengan Prisma sebagai ORM.
|
|
||||||
|
|
||||||
Seluruh akses database dilakukan melalui Repository.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Adapter Layer
|
|
||||||
|
|
||||||
Integrasi dengan layanan eksternal dilakukan melalui Adapter.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
- Nextcloud Adapter
|
|
||||||
- Jellyfin Adapter
|
|
||||||
- Immich Adapter
|
|
||||||
- Minecraft Adapter
|
|
||||||
|
|
||||||
Application Service tidak boleh memanggil API eksternal secara langsung.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Contoh Alur Create User
|
|
||||||
|
|
||||||
``` text
|
|
||||||
POST /users
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
UsersController
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
CreateUserService
|
|
||||||
│
|
|
||||||
├─────────────┐
|
|
||||||
▼ ▼
|
|
||||||
UserDomain GroupDomain
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
UserRepository
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
PostgreSQL
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Media Adapter
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Audit Service
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
HTTP Response
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Dependency Rule
|
|
||||||
|
|
||||||
Layer Boleh Memanggil
|
|
||||||
--------------------- -------------------------------------
|
|
||||||
Controller Application Service
|
|
||||||
Application Service Domain Service, Repository, Adapter
|
|
||||||
Domain Service Entity
|
|
||||||
Repository Prisma
|
|
||||||
Adapter External API
|
|
||||||
Entity Tidak memanggil layer lain
|
|
||||||
|
|
||||||
Dependensi selalu mengarah ke bawah.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Struktur Folder
|
|
||||||
|
|
||||||
``` text
|
|
||||||
src/
|
|
||||||
│
|
|
||||||
├── modules/
|
|
||||||
│ ├── users/
|
|
||||||
│ │ ├── controllers/
|
|
||||||
│ │ ├── services/
|
|
||||||
│ │ ├── domain/
|
|
||||||
│ │ ├── repositories/
|
|
||||||
│ │ ├── dto/
|
|
||||||
│ │ ├── entities/
|
|
||||||
│ │ └── users.module.ts
|
|
||||||
│ │
|
|
||||||
│ ├── groups/
|
|
||||||
│ ├── authorization/
|
|
||||||
│ ├── registry/
|
|
||||||
│ ├── media/
|
|
||||||
│ ├── storage/
|
|
||||||
│ └── audit/
|
|
||||||
│
|
|
||||||
├── adapters/
|
|
||||||
│ ├── nextcloud/
|
|
||||||
│ ├── jellyfin/
|
|
||||||
│ ├── immich/
|
|
||||||
│ └── ...
|
|
||||||
│
|
|
||||||
├── common/
|
|
||||||
│ ├── auth/
|
|
||||||
│ ├── guards/
|
|
||||||
│ ├── filters/
|
|
||||||
│ ├── middleware/
|
|
||||||
│ ├── logger/
|
|
||||||
│ └── utils/
|
|
||||||
│
|
|
||||||
├── prisma/
|
|
||||||
└── main.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Aturan Implementasi
|
|
||||||
|
|
||||||
1. Business logic tidak boleh berada di Controller.
|
|
||||||
2. Frontend tidak boleh menyimpan business logic.
|
|
||||||
3. Integrasi eksternal wajib melalui Adapter.
|
|
||||||
4. Semua akses database melalui Repository.
|
|
||||||
5. Setiap modul harus dapat diuji secara mandiri.
|
|
||||||
6. Setiap layer memiliki satu tanggung jawab utama.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Hasil Akhir Fase 3
|
|
||||||
|
|
||||||
Setelah fase ini selesai, RayLab Core memiliki standar implementasi yang
|
|
||||||
konsisten untuk seluruh modul sehingga pengembangan jangka panjang
|
|
||||||
menjadi lebih mudah, terstruktur, dan mudah dipelihara.
|
|
||||||
@@ -1,311 +0,0 @@
|
|||||||
# RayLab Core Phase 4 --- Technical Foundation
|
|
||||||
|
|
||||||
## Tujuan
|
|
||||||
|
|
||||||
Fase 4 menetapkan fondasi teknis RayLab Core. Dokumen ini mendefinisikan
|
|
||||||
teknologi, konvensi pengembangan, dan standar implementasi yang akan
|
|
||||||
digunakan secara konsisten pada seluruh modul.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Tujuan Akhir
|
|
||||||
|
|
||||||
Project dapat dijalankan dengan satu perintah:
|
|
||||||
|
|
||||||
``` bash
|
|
||||||
docker compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
Kemudian endpoint health dapat diakses:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
GET /health
|
|
||||||
```
|
|
||||||
|
|
||||||
Response:
|
|
||||||
|
|
||||||
``` json
|
|
||||||
{
|
|
||||||
"status": "ok"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Technology Stack
|
|
||||||
|
|
||||||
## Backend Framework
|
|
||||||
|
|
||||||
**NestJS**
|
|
||||||
|
|
||||||
Alasan:
|
|
||||||
|
|
||||||
- Modular architecture
|
|
||||||
- Dependency Injection
|
|
||||||
- Guard, Middleware, Interceptor
|
|
||||||
- Swagger integration
|
|
||||||
- Testing support
|
|
||||||
- Sangat cocok untuk aplikasi enterprise.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Programming Language
|
|
||||||
|
|
||||||
**TypeScript**
|
|
||||||
|
|
||||||
Keuntungan:
|
|
||||||
|
|
||||||
- Type safety
|
|
||||||
- Maintainability
|
|
||||||
- IDE support
|
|
||||||
- Konsisten dengan NestJS
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Database
|
|
||||||
|
|
||||||
**PostgreSQL**
|
|
||||||
|
|
||||||
Digunakan sebagai database utama RayLab Core.
|
|
||||||
|
|
||||||
Semua data aplikasi disimpan di PostgreSQL.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## ORM
|
|
||||||
|
|
||||||
**Prisma**
|
|
||||||
|
|
||||||
Keuntungan:
|
|
||||||
|
|
||||||
- Type-safe query
|
|
||||||
- Migration
|
|
||||||
- Schema management
|
|
||||||
- Autocomplete
|
|
||||||
- Maintainable
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Cache
|
|
||||||
|
|
||||||
**Redis**
|
|
||||||
|
|
||||||
Digunakan untuk:
|
|
||||||
|
|
||||||
- Session
|
|
||||||
- Cache
|
|
||||||
- Rate limiting
|
|
||||||
- Queue (apabila diperlukan)
|
|
||||||
|
|
||||||
Redis bukan penyimpanan data utama.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## API Documentation
|
|
||||||
|
|
||||||
**Swagger (OpenAPI)**
|
|
||||||
|
|
||||||
Seluruh endpoint wajib memiliki dokumentasi.
|
|
||||||
|
|
||||||
Minimal mencakup:
|
|
||||||
|
|
||||||
- Summary
|
|
||||||
- Description
|
|
||||||
- Request DTO
|
|
||||||
- Response DTO
|
|
||||||
- Error Response
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Validation
|
|
||||||
|
|
||||||
Menggunakan:
|
|
||||||
|
|
||||||
- class-validator
|
|
||||||
- class-transformer
|
|
||||||
|
|
||||||
Seluruh request divalidasi sebelum masuk business logic.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Logging
|
|
||||||
|
|
||||||
Menggunakan:
|
|
||||||
|
|
||||||
**Pino**
|
|
||||||
|
|
||||||
Target:
|
|
||||||
|
|
||||||
- Structured Logging
|
|
||||||
- Performance
|
|
||||||
- Mudah diintegrasikan dengan monitoring.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
Standar pengujian:
|
|
||||||
|
|
||||||
### Unit Test
|
|
||||||
|
|
||||||
- Jest
|
|
||||||
|
|
||||||
### Integration Test
|
|
||||||
|
|
||||||
- Jest
|
|
||||||
|
|
||||||
### End-to-End Test
|
|
||||||
|
|
||||||
- Playwright
|
|
||||||
|
|
||||||
Standar ini mengikuti roadmap pengujian RayLab.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Containerization
|
|
||||||
|
|
||||||
Seluruh aplikasi dijalankan menggunakan Docker.
|
|
||||||
|
|
||||||
Container utama:
|
|
||||||
|
|
||||||
- raylab-core
|
|
||||||
- postgres-raylab
|
|
||||||
- redis
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
Konfigurasi menggunakan:
|
|
||||||
|
|
||||||
- .env
|
|
||||||
- NestJS Config Module
|
|
||||||
|
|
||||||
Business logic tidak boleh mengakses process.env secara langsung.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Coding Convention
|
|
||||||
|
|
||||||
## Dependency Injection
|
|
||||||
|
|
||||||
Semua dependency menggunakan Dependency Injection NestJS.
|
|
||||||
|
|
||||||
Tidak diperbolehkan:
|
|
||||||
|
|
||||||
``` typescript
|
|
||||||
new UserRepository();
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Naming Convention
|
|
||||||
|
|
||||||
Folder:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
users
|
|
||||||
groups
|
|
||||||
authorization
|
|
||||||
registry
|
|
||||||
```
|
|
||||||
|
|
||||||
Class:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
UserService
|
|
||||||
UsersController
|
|
||||||
UserRepository
|
|
||||||
CreateUserDto
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## UUID
|
|
||||||
|
|
||||||
Seluruh entity menggunakan UUID v7 sebagai primary key.
|
|
||||||
|
|
||||||
Tidak menggunakan integer auto increment.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## API Versioning
|
|
||||||
|
|
||||||
Semua endpoint menggunakan format:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
/api/v1/
|
|
||||||
```
|
|
||||||
|
|
||||||
Perubahan besar menggunakan versi API baru.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Standard Response
|
|
||||||
|
|
||||||
### Success
|
|
||||||
|
|
||||||
``` json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"data": {},
|
|
||||||
"meta": {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Error
|
|
||||||
|
|
||||||
``` json
|
|
||||||
{
|
|
||||||
"success": false,
|
|
||||||
"error": {
|
|
||||||
"code": "USER_NOT_FOUND",
|
|
||||||
"message": "User not found"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Development Convention
|
|
||||||
|
|
||||||
Seluruh modul wajib mengikuti aturan berikut.
|
|
||||||
|
|
||||||
- Satu module mewakili satu domain bisnis.
|
|
||||||
- Tidak ada query database di Controller.
|
|
||||||
- Tidak ada business logic di Frontend.
|
|
||||||
- Integrasi eksternal hanya melalui Adapter.
|
|
||||||
- Seluruh endpoint memiliki DTO.
|
|
||||||
- Seluruh endpoint memiliki validasi.
|
|
||||||
- Seluruh endpoint memiliki dokumentasi Swagger.
|
|
||||||
- Seluruh perubahan penting menghasilkan Audit Log.
|
|
||||||
- Seluruh modul dapat diuji secara mandiri.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Struktur Project
|
|
||||||
|
|
||||||
``` text
|
|
||||||
raylab-core/
|
|
||||||
|
|
||||||
├── src/
|
|
||||||
├── prisma/
|
|
||||||
├── docs/
|
|
||||||
├── tests/
|
|
||||||
├── scripts/
|
|
||||||
├── docker/
|
|
||||||
├── package.json
|
|
||||||
├── docker-compose.yml
|
|
||||||
└── README.md
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Hasil Akhir Fase 4
|
|
||||||
|
|
||||||
Setelah fase ini selesai, RayLab Core memiliki fondasi teknis yang
|
|
||||||
konsisten, modern, dan siap digunakan sebagai dasar implementasi seluruh
|
|
||||||
modul pada fase berikutnya.
|
|
||||||
|
|
||||||
Dokumen ini menjadi acuan resmi untuk pemilihan teknologi, standar
|
|
||||||
coding, struktur project, dan konvensi pengembangan RayLab Core.
|
|
||||||
@@ -1,310 +0,0 @@
|
|||||||
# RayLab Core Phase 5 --- Data Architecture
|
|
||||||
|
|
||||||
## Tujuan
|
|
||||||
|
|
||||||
Fase 5 mendefinisikan arsitektur data RayLab Core. Fokus fase ini adalah
|
|
||||||
bagaimana seluruh domain saling berhubungan pada tingkat penyimpanan
|
|
||||||
data, sekaligus menetapkan standar desain database yang akan digunakan
|
|
||||||
oleh seluruh modul.
|
|
||||||
|
|
||||||
> Dokumen ini membahas arsitektur data, bukan implementasi tabel secara
|
|
||||||
> rinci.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Prinsip Data Architecture
|
|
||||||
|
|
||||||
RayLab Core menggunakan prinsip berikut:
|
|
||||||
|
|
||||||
- Database merepresentasikan domain bisnis.
|
|
||||||
- Tidak ada duplikasi data antar domain.
|
|
||||||
- Setiap domain memiliki kepemilikan data (data ownership).
|
|
||||||
- Seluruh perubahan struktur database dilakukan melalui migration.
|
|
||||||
- Integritas data lebih diutamakan daripada kemudahan implementasi.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Pembagian Database Schema
|
|
||||||
|
|
||||||
Agar database tetap terstruktur, PostgreSQL dibagi menjadi beberapa
|
|
||||||
schema.
|
|
||||||
|
|
||||||
## identity
|
|
||||||
|
|
||||||
Mengelola identitas pengguna.
|
|
||||||
|
|
||||||
Entity utama:
|
|
||||||
|
|
||||||
- users
|
|
||||||
- profiles
|
|
||||||
- sessions
|
|
||||||
- user_groups
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## authorization
|
|
||||||
|
|
||||||
Mengelola hak akses.
|
|
||||||
|
|
||||||
Entity utama:
|
|
||||||
|
|
||||||
- groups
|
|
||||||
- roles
|
|
||||||
- permissions
|
|
||||||
- role_permissions
|
|
||||||
- group_roles
|
|
||||||
- special_access
|
|
||||||
- domain_access
|
|
||||||
- resource_access
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## registry
|
|
||||||
|
|
||||||
Mengelola registrasi aplikasi dan layanan.
|
|
||||||
|
|
||||||
Entity utama:
|
|
||||||
|
|
||||||
- applications
|
|
||||||
- services
|
|
||||||
- domains
|
|
||||||
- adapters
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## configuration
|
|
||||||
|
|
||||||
Mengelola konfigurasi runtime.
|
|
||||||
|
|
||||||
Entity utama:
|
|
||||||
|
|
||||||
- settings
|
|
||||||
- setting_categories
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## storage
|
|
||||||
|
|
||||||
Mengelola penyimpanan logis.
|
|
||||||
|
|
||||||
Entity utama:
|
|
||||||
|
|
||||||
- storages
|
|
||||||
- volumes
|
|
||||||
- quotas
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## media
|
|
||||||
|
|
||||||
Digunakan oleh Media Manager.
|
|
||||||
|
|
||||||
Entity utama:
|
|
||||||
|
|
||||||
- libraries
|
|
||||||
- folder_templates
|
|
||||||
- folder_permissions
|
|
||||||
- folder_mappings
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## audit
|
|
||||||
|
|
||||||
Menyimpan riwayat aktivitas.
|
|
||||||
|
|
||||||
Entity utama:
|
|
||||||
|
|
||||||
- audit_logs
|
|
||||||
- audit_categories
|
|
||||||
- audit_targets
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## system
|
|
||||||
|
|
||||||
Digunakan oleh sistem internal.
|
|
||||||
|
|
||||||
Entity utama:
|
|
||||||
|
|
||||||
- jobs
|
|
||||||
- notifications
|
|
||||||
- health_checks
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Hubungan Antar Domain
|
|
||||||
|
|
||||||
``` text
|
|
||||||
Identity
|
|
||||||
│
|
|
||||||
└──── Authorization
|
|
||||||
│
|
|
||||||
├──── Registry
|
|
||||||
├──── Storage
|
|
||||||
├──── Media
|
|
||||||
└──── Configuration
|
|
||||||
|
|
||||||
Semua Domain
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Audit
|
|
||||||
```
|
|
||||||
|
|
||||||
Audit menerima aktivitas dari seluruh domain.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Standar Primary Key
|
|
||||||
|
|
||||||
Seluruh entity menggunakan:
|
|
||||||
|
|
||||||
- UUID v7
|
|
||||||
|
|
||||||
Keuntungan:
|
|
||||||
|
|
||||||
- Sulit ditebak
|
|
||||||
- Konsisten
|
|
||||||
- Cocok untuk sistem terdistribusi
|
|
||||||
- Performa indeks lebih baik dibanding UUID v4
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Standar Timestamp
|
|
||||||
|
|
||||||
Seluruh entity memiliki minimal:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
created_at
|
|
||||||
updated_at
|
|
||||||
deleted_at
|
|
||||||
```
|
|
||||||
|
|
||||||
Jika diperlukan juga memiliki:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
created_by
|
|
||||||
updated_by
|
|
||||||
deleted_by
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Soft Delete
|
|
||||||
|
|
||||||
Data penting tidak langsung dihapus.
|
|
||||||
|
|
||||||
Sebaliknya menggunakan:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
deleted_at
|
|
||||||
```
|
|
||||||
|
|
||||||
Keuntungan:
|
|
||||||
|
|
||||||
- Audit lebih mudah
|
|
||||||
- Restore data memungkinkan
|
|
||||||
- Riwayat tetap terjaga
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Metadata
|
|
||||||
|
|
||||||
Entity yang memerlukan fleksibilitas dapat memiliki kolom:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
metadata (JSONB)
|
|
||||||
```
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
``` json
|
|
||||||
{
|
|
||||||
"theme": "dark",
|
|
||||||
"avatar": "/avatars/user.png"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
JSONB digunakan hanya untuk data yang bersifat dinamis, bukan untuk
|
|
||||||
relasi utama.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Naming Convention
|
|
||||||
|
|
||||||
## Schema
|
|
||||||
|
|
||||||
Menggunakan huruf kecil.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
identity
|
|
||||||
authorization
|
|
||||||
audit
|
|
||||||
```
|
|
||||||
|
|
||||||
## Table
|
|
||||||
|
|
||||||
Menggunakan bentuk jamak.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
users
|
|
||||||
roles
|
|
||||||
permissions
|
|
||||||
```
|
|
||||||
|
|
||||||
## Column
|
|
||||||
|
|
||||||
Menggunakan snake_case.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
created_at
|
|
||||||
updated_at
|
|
||||||
display_name
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Migration Strategy
|
|
||||||
|
|
||||||
Seluruh perubahan struktur database wajib menggunakan Prisma Migration.
|
|
||||||
|
|
||||||
Tidak diperbolehkan:
|
|
||||||
|
|
||||||
- Mengubah tabel langsung di production.
|
|
||||||
- Mengedit struktur database secara manual.
|
|
||||||
- Menghapus migration yang sudah digunakan.
|
|
||||||
|
|
||||||
Migration menjadi sumber kebenaran (single source of truth) untuk
|
|
||||||
struktur database.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Integritas Data
|
|
||||||
|
|
||||||
Beberapa prinsip yang harus dijaga:
|
|
||||||
|
|
||||||
- Foreign key digunakan pada seluruh relasi penting.
|
|
||||||
- Constraint digunakan untuk menjaga validitas data.
|
|
||||||
- Unique index diterapkan pada data yang harus unik.
|
|
||||||
- Cascade delete hanya digunakan jika benar-benar diperlukan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Hasil Akhir Fase 5
|
|
||||||
|
|
||||||
Pada akhir fase ini RayLab Core memiliki:
|
|
||||||
|
|
||||||
- Standar arsitektur database.
|
|
||||||
- Pembagian schema berdasarkan domain.
|
|
||||||
- Aturan relasi data.
|
|
||||||
- Standar UUID, timestamp, dan soft delete.
|
|
||||||
- Strategi migration.
|
|
||||||
- Konvensi penamaan database.
|
|
||||||
|
|
||||||
Dokumen ini menjadi acuan resmi ketika mulai membuat Prisma Schema dan
|
|
||||||
migration pada tahap implementasi.
|
|
||||||
@@ -1,289 +0,0 @@
|
|||||||
# RayLab Core Phase 6 --- API Architecture
|
|
||||||
|
|
||||||
## Tujuan
|
|
||||||
|
|
||||||
Fase 6 mendefinisikan standar API RayLab Core sebagai kontrak komunikasi
|
|
||||||
untuk seluruh aplikasi dalam ekosistem RayLab.
|
|
||||||
|
|
||||||
API harus konsisten, mudah dipahami, terdokumentasi, dan stabil untuk
|
|
||||||
jangka panjang.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Prinsip API
|
|
||||||
|
|
||||||
Seluruh API RayLab Core mengikuti prinsip berikut:
|
|
||||||
|
|
||||||
- RESTful
|
|
||||||
- Stateless
|
|
||||||
- Versioned
|
|
||||||
- Consistent
|
|
||||||
- Predictable
|
|
||||||
- Self Documented
|
|
||||||
- API First
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Base URL
|
|
||||||
|
|
||||||
Seluruh endpoint menggunakan format:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
/api/v1/
|
|
||||||
```
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
/api/v1/users
|
|
||||||
/api/v1/groups
|
|
||||||
/api/v1/domains
|
|
||||||
/api/v1/applications
|
|
||||||
/api/v1/media
|
|
||||||
/api/v1/storage
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# HTTP Method
|
|
||||||
|
|
||||||
Method Fungsi
|
|
||||||
-------- ------------------------------------------
|
|
||||||
GET Membaca data
|
|
||||||
POST Membuat data
|
|
||||||
PATCH Memperbarui sebagian data
|
|
||||||
PUT Mengganti seluruh data (jika diperlukan)
|
|
||||||
DELETE Soft delete
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Resource-Oriented API
|
|
||||||
|
|
||||||
Contoh endpoint User:
|
|
||||||
|
|
||||||
``` http
|
|
||||||
GET /api/v1/users
|
|
||||||
GET /api/v1/users/{id}
|
|
||||||
POST /api/v1/users
|
|
||||||
PATCH /api/v1/users/{id}
|
|
||||||
DELETE /api/v1/users/{id}
|
|
||||||
```
|
|
||||||
|
|
||||||
Nested resource:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
/users/{id}/groups
|
|
||||||
/users/{id}/sessions
|
|
||||||
/groups/{id}/roles
|
|
||||||
/domains/{id}/resources
|
|
||||||
```
|
|
||||||
|
|
||||||
Action endpoint:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
/users/{id}/reset-password
|
|
||||||
/users/{id}/special-access
|
|
||||||
/users/{id}/groups
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Standard Response
|
|
||||||
|
|
||||||
## Success
|
|
||||||
|
|
||||||
``` json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"data": {},
|
|
||||||
"meta": {
|
|
||||||
"requestId": "...",
|
|
||||||
"timestamp": "..."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Error
|
|
||||||
|
|
||||||
``` json
|
|
||||||
{
|
|
||||||
"success": false,
|
|
||||||
"error": {
|
|
||||||
"code": "USER_NOT_FOUND",
|
|
||||||
"message": "User not found"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Semua endpoint wajib menggunakan format yang sama.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Pagination
|
|
||||||
|
|
||||||
Request:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
GET /users?page=1&limit=20
|
|
||||||
```
|
|
||||||
|
|
||||||
Response:
|
|
||||||
|
|
||||||
``` json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"data": [],
|
|
||||||
"pagination": {
|
|
||||||
"page": 1,
|
|
||||||
"limit": 20,
|
|
||||||
"total": 125,
|
|
||||||
"totalPages": 7
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Filtering
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
/users?group=Owner
|
|
||||||
/applications?status=online
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Sorting
|
|
||||||
|
|
||||||
``` text
|
|
||||||
/users?sort=name
|
|
||||||
/users?sort=-createdAt
|
|
||||||
```
|
|
||||||
|
|
||||||
Tanda minus (`-`) berarti urutan menurun (descending).
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Search
|
|
||||||
|
|
||||||
``` text
|
|
||||||
/users?search=ray
|
|
||||||
```
|
|
||||||
|
|
||||||
Pencarian dilakukan melalui query parameter, bukan endpoint terpisah.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# API Versioning
|
|
||||||
|
|
||||||
Versi API menjadi bagian dari URL.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
/api/v1/
|
|
||||||
/api/v2/
|
|
||||||
```
|
|
||||||
|
|
||||||
Perubahan besar tidak boleh merusak kompatibilitas client lama.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Public API dan Internal API
|
|
||||||
|
|
||||||
## Public API
|
|
||||||
|
|
||||||
Digunakan oleh:
|
|
||||||
|
|
||||||
- Web Admin
|
|
||||||
- Android
|
|
||||||
- CLI
|
|
||||||
- Future Client
|
|
||||||
|
|
||||||
## Internal API
|
|
||||||
|
|
||||||
Digunakan oleh:
|
|
||||||
|
|
||||||
- Background Job
|
|
||||||
- Scheduler
|
|
||||||
- Adapter
|
|
||||||
- Internal Service
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
/internal/jobs
|
|
||||||
/internal/sync
|
|
||||||
```
|
|
||||||
|
|
||||||
Internal API tidak boleh diekspos ke publik.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Request ID
|
|
||||||
|
|
||||||
Setiap request memiliki Request ID unik.
|
|
||||||
|
|
||||||
Header:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
X-Request-ID
|
|
||||||
```
|
|
||||||
|
|
||||||
Request ID digunakan untuk:
|
|
||||||
|
|
||||||
- Audit
|
|
||||||
- Logging
|
|
||||||
- Debugging
|
|
||||||
- Distributed tracing
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Idempotency
|
|
||||||
|
|
||||||
Operasi tertentu mendukung:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
Idempotency-Key
|
|
||||||
```
|
|
||||||
|
|
||||||
Hal ini mencegah duplikasi request akibat retry jaringan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Dokumentasi API
|
|
||||||
|
|
||||||
Seluruh endpoint harus didokumentasikan menggunakan Swagger (OpenAPI).
|
|
||||||
|
|
||||||
Minimal mencakup:
|
|
||||||
|
|
||||||
- Summary
|
|
||||||
- Description
|
|
||||||
- Request DTO
|
|
||||||
- Response DTO
|
|
||||||
- Error Response
|
|
||||||
|
|
||||||
Tidak boleh ada endpoint tanpa dokumentasi.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Aturan API
|
|
||||||
|
|
||||||
- Tidak ada endpoint tanpa versioning.
|
|
||||||
- Tidak ada endpoint tanpa validasi DTO.
|
|
||||||
- Tidak ada endpoint tanpa dokumentasi.
|
|
||||||
- Seluruh endpoint menghasilkan response yang konsisten.
|
|
||||||
- Controller tidak berisi business logic.
|
|
||||||
- Error menggunakan kode yang konsisten.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Hasil Akhir Fase 6
|
|
||||||
|
|
||||||
Pada akhir fase ini RayLab Core memiliki standar API yang menjadi
|
|
||||||
kontrak komunikasi resmi untuk seluruh aplikasi dalam ekosistem RayLab.
|
|
||||||
|
|
||||||
Dokumen ini menjadi acuan implementasi Controller, DTO, Service,
|
|
||||||
dokumentasi Swagger, dan integrasi seluruh client dengan RayLab Core.
|
|
||||||
@@ -1,249 +0,0 @@
|
|||||||
# RayLab Core Phase 7 --- Project Structure
|
|
||||||
|
|
||||||
## Tujuan
|
|
||||||
|
|
||||||
Fase 7 mendefinisikan struktur repository, organisasi source code, serta
|
|
||||||
standar penempatan file pada RayLab Core. Tujuannya adalah memastikan
|
|
||||||
seluruh kode memiliki pola yang konsisten sehingga mudah dipelihara,
|
|
||||||
dikembangkan, dan dipahami.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Prinsip
|
|
||||||
|
|
||||||
- Struktur mengikuti domain bisnis.
|
|
||||||
- Setiap module berdiri sendiri.
|
|
||||||
- Konfigurasi dipisahkan dari business logic.
|
|
||||||
- Dokumentasi menjadi bagian dari repository.
|
|
||||||
- Mudah diskalakan tanpa mengubah struktur dasar.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Struktur Repository
|
|
||||||
|
|
||||||
``` text
|
|
||||||
raylab-core/
|
|
||||||
│
|
|
||||||
├── docs/
|
|
||||||
├── docker/
|
|
||||||
├── prisma/
|
|
||||||
├── scripts/
|
|
||||||
├── tests/
|
|
||||||
├── src/
|
|
||||||
├── .env.example
|
|
||||||
├── .gitignore
|
|
||||||
├── docker-compose.yml
|
|
||||||
├── package.json
|
|
||||||
├── tsconfig.json
|
|
||||||
└── README.md
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Struktur Source Code
|
|
||||||
|
|
||||||
``` text
|
|
||||||
src/
|
|
||||||
│
|
|
||||||
├── main.ts
|
|
||||||
├── app.module.ts
|
|
||||||
├── common/
|
|
||||||
├── config/
|
|
||||||
├── modules/
|
|
||||||
├── adapters/
|
|
||||||
└── shared/
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Common
|
|
||||||
|
|
||||||
Berisi komponen yang digunakan lintas modul.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
- Guards
|
|
||||||
- Filters
|
|
||||||
- Interceptors
|
|
||||||
- Middleware
|
|
||||||
- Logger
|
|
||||||
- Exceptions
|
|
||||||
- Decorators
|
|
||||||
- Pipes
|
|
||||||
- Utilities
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Config
|
|
||||||
|
|
||||||
Berisi konfigurasi aplikasi.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
- Database
|
|
||||||
- Redis
|
|
||||||
- JWT
|
|
||||||
- Environment
|
|
||||||
- Swagger
|
|
||||||
|
|
||||||
Seluruh konfigurasi diakses melalui ConfigService.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Modules
|
|
||||||
|
|
||||||
Setiap domain memiliki module sendiri.
|
|
||||||
|
|
||||||
``` text
|
|
||||||
modules/
|
|
||||||
├── identity/
|
|
||||||
├── authorization/
|
|
||||||
├── registry/
|
|
||||||
├── configuration/
|
|
||||||
├── storage/
|
|
||||||
├── media/
|
|
||||||
├── audit/
|
|
||||||
├── dashboard/
|
|
||||||
└── health/
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Struktur Internal Module
|
|
||||||
|
|
||||||
``` text
|
|
||||||
identity/
|
|
||||||
│
|
|
||||||
├── controllers/
|
|
||||||
├── services/
|
|
||||||
├── domain/
|
|
||||||
├── repositories/
|
|
||||||
├── dto/
|
|
||||||
├── entities/
|
|
||||||
├── interfaces/
|
|
||||||
├── mappers/
|
|
||||||
├── events/
|
|
||||||
└── identity.module.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Seluruh file yang berkaitan dengan satu domain berada dalam module yang
|
|
||||||
sama.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Adapters
|
|
||||||
|
|
||||||
Seluruh integrasi eksternal ditempatkan pada folder adapters.
|
|
||||||
|
|
||||||
``` text
|
|
||||||
adapters/
|
|
||||||
├── nextcloud/
|
|
||||||
├── jellyfin/
|
|
||||||
├── immich/
|
|
||||||
├── minecraft/
|
|
||||||
└── ...
|
|
||||||
```
|
|
||||||
|
|
||||||
Adapter tidak menyimpan business logic.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Prisma
|
|
||||||
|
|
||||||
``` text
|
|
||||||
prisma/
|
|
||||||
├── schema.prisma
|
|
||||||
├── migrations/
|
|
||||||
└── seed.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Seluruh perubahan database dilakukan melalui migration.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Tests
|
|
||||||
|
|
||||||
``` text
|
|
||||||
tests/
|
|
||||||
├── unit/
|
|
||||||
├── integration/
|
|
||||||
└── e2e/
|
|
||||||
```
|
|
||||||
|
|
||||||
Pengujian dipisahkan berdasarkan jenisnya.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Dokumentasi
|
|
||||||
|
|
||||||
``` text
|
|
||||||
docs/
|
|
||||||
├── architecture/
|
|
||||||
├── adr/
|
|
||||||
├── api/
|
|
||||||
├── modules/
|
|
||||||
└── guides/
|
|
||||||
```
|
|
||||||
|
|
||||||
Dokumentasi menjadi bagian dari source repository dan diperbarui
|
|
||||||
bersamaan dengan perubahan kode.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Naming Convention
|
|
||||||
|
|
||||||
## Folder
|
|
||||||
|
|
||||||
Menggunakan huruf kecil.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
identity
|
|
||||||
authorization
|
|
||||||
media
|
|
||||||
```
|
|
||||||
|
|
||||||
## File
|
|
||||||
|
|
||||||
Menggunakan nama yang deskriptif.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
create-user.dto.ts
|
|
||||||
user.repository.ts
|
|
||||||
users.controller.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
## Class
|
|
||||||
|
|
||||||
Menggunakan PascalCase.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
UsersController
|
|
||||||
CreateUserDto
|
|
||||||
UserRepository
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Aturan Penempatan Kode
|
|
||||||
|
|
||||||
- Controller hanya berada di folder controllers.
|
|
||||||
- DTO hanya berada di folder dto.
|
|
||||||
- Entity hanya berada di folder entities.
|
|
||||||
- Repository hanya berada di folder repositories.
|
|
||||||
- Business logic berada di services dan domain.
|
|
||||||
- Integrasi eksternal berada di adapters.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Hasil Akhir Fase 7
|
|
||||||
|
|
||||||
Pada akhir fase ini, RayLab Core memiliki struktur repository dan
|
|
||||||
organisasi kode yang baku. Setiap developer dapat mengetahui lokasi
|
|
||||||
suatu komponen tanpa perlu menebak, sehingga pengembangan jangka panjang
|
|
||||||
tetap konsisten dan mudah dipelihara.
|
|
||||||
@@ -1,267 +0,0 @@
|
|||||||
# RayLab Core Phase 8 --- Development Workflow
|
|
||||||
|
|
||||||
## Tujuan
|
|
||||||
|
|
||||||
Fase 8 mendefinisikan alur kerja pengembangan (Development Workflow)
|
|
||||||
RayLab Core agar proses implementasi, pengujian, review, dan rilis
|
|
||||||
dilakukan secara konsisten.
|
|
||||||
|
|
||||||
Dokumen ini menjadi standar kerja untuk seluruh pengembangan RayLab
|
|
||||||
Core.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Prinsip
|
|
||||||
|
|
||||||
- Seluruh perubahan dapat dilacak.
|
|
||||||
- Setiap fitur dikembangkan secara terpisah.
|
|
||||||
- Kualitas kode lebih penting daripada kecepatan.
|
|
||||||
- Otomatisasi digunakan sebanyak mungkin.
|
|
||||||
- Dokumentasi diperbarui bersamaan dengan perubahan kode.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Development Lifecycle
|
|
||||||
|
|
||||||
``` text
|
|
||||||
Requirement
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Architecture
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Issue / Task
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Implementation
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Unit Test
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Integration Test
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
End-to-End Test
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Code Review
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Merge
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Release
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Git Workflow
|
|
||||||
|
|
||||||
## Branch Utama
|
|
||||||
|
|
||||||
``` text
|
|
||||||
main
|
|
||||||
```
|
|
||||||
|
|
||||||
Selalu berisi kode yang stabil.
|
|
||||||
|
|
||||||
## Branch Pengembangan
|
|
||||||
|
|
||||||
Gunakan branch terpisah untuk setiap pekerjaan.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
feature/identity
|
|
||||||
feature/media-manager
|
|
||||||
feature/api-users
|
|
||||||
|
|
||||||
fix/login-session
|
|
||||||
fix/storage-permission
|
|
||||||
|
|
||||||
refactor/authorization
|
|
||||||
|
|
||||||
docs/api-architecture
|
|
||||||
```
|
|
||||||
|
|
||||||
Satu branch hanya menangani satu tujuan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Commit Convention
|
|
||||||
|
|
||||||
Format commit:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
type(scope): description
|
|
||||||
```
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
feat(identity): add user service
|
|
||||||
fix(media): resolve folder permission bug
|
|
||||||
docs(api): update endpoint documentation
|
|
||||||
refactor(registry): simplify adapter registration
|
|
||||||
test(users): add integration test
|
|
||||||
```
|
|
||||||
|
|
||||||
Jenis commit:
|
|
||||||
|
|
||||||
- feat
|
|
||||||
- fix
|
|
||||||
- docs
|
|
||||||
- refactor
|
|
||||||
- test
|
|
||||||
- chore
|
|
||||||
- ci
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Pull Request Checklist
|
|
||||||
|
|
||||||
Sebelum merge:
|
|
||||||
|
|
||||||
- Kode berhasil dikompilasi.
|
|
||||||
- Unit test lulus.
|
|
||||||
- Integration test lulus.
|
|
||||||
- E2E test lulus (jika relevan).
|
|
||||||
- Dokumentasi diperbarui.
|
|
||||||
- Migration ditinjau.
|
|
||||||
- Tidak ada perubahan yang tidak terkait.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Testing Strategy
|
|
||||||
|
|
||||||
## Unit Test
|
|
||||||
|
|
||||||
Menguji business logic secara terisolasi.
|
|
||||||
|
|
||||||
Tool:
|
|
||||||
|
|
||||||
- Jest
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Integration Test
|
|
||||||
|
|
||||||
Menguji interaksi antar modul.
|
|
||||||
|
|
||||||
Tool:
|
|
||||||
|
|
||||||
- Jest
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## End-to-End Test
|
|
||||||
|
|
||||||
Menguji perilaku sistem dari sudut pandang pengguna.
|
|
||||||
|
|
||||||
Tool:
|
|
||||||
|
|
||||||
- Playwright
|
|
||||||
|
|
||||||
Standar ini mengikuti proses pengujian RayLab yang telah ditetapkan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Code Review
|
|
||||||
|
|
||||||
Fokus review:
|
|
||||||
|
|
||||||
- Kesesuaian dengan arsitektur.
|
|
||||||
- Kualitas business logic.
|
|
||||||
- Keamanan.
|
|
||||||
- Konsistensi penamaan.
|
|
||||||
- Keterbacaan kode.
|
|
||||||
- Dampak terhadap modul lain.
|
|
||||||
|
|
||||||
Review tidak hanya memeriksa apakah kode berjalan, tetapi juga apakah
|
|
||||||
kode sesuai dengan standar RayLab Core.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Database Workflow
|
|
||||||
|
|
||||||
Perubahan database:
|
|
||||||
|
|
||||||
1. Ubah `schema.prisma`.
|
|
||||||
2. Buat migration.
|
|
||||||
3. Jalankan migration pada lingkungan pengembangan.
|
|
||||||
4. Perbarui dokumentasi jika diperlukan.
|
|
||||||
|
|
||||||
Perubahan langsung pada database produksi tidak diperbolehkan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Documentation Workflow
|
|
||||||
|
|
||||||
Setiap perubahan yang memengaruhi arsitektur, API, atau modul harus
|
|
||||||
diikuti dengan pembaruan dokumentasi.
|
|
||||||
|
|
||||||
Area dokumentasi:
|
|
||||||
|
|
||||||
- Architecture
|
|
||||||
- API
|
|
||||||
- Module
|
|
||||||
- ADR
|
|
||||||
- Guide
|
|
||||||
|
|
||||||
Dokumentasi menjadi bagian dari proses pengembangan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Release Workflow
|
|
||||||
|
|
||||||
``` text
|
|
||||||
Development
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Testing
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Review
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Tag Release
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Docker Build
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Deployment
|
|
||||||
```
|
|
||||||
|
|
||||||
Setiap rilis menggunakan tag versi yang jelas.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
v1.0.0
|
|
||||||
v1.1.0
|
|
||||||
v2.0.0
|
|
||||||
```
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Definition of Done
|
|
||||||
|
|
||||||
Sebuah pekerjaan dianggap selesai apabila:
|
|
||||||
|
|
||||||
- Implementasi selesai.
|
|
||||||
- Seluruh test lulus.
|
|
||||||
- Tidak ada error linting.
|
|
||||||
- Dokumentasi diperbarui.
|
|
||||||
- Migration tersedia (jika diperlukan).
|
|
||||||
- Telah melalui code review.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Hasil Akhir Fase 8
|
|
||||||
|
|
||||||
Pada akhir fase ini RayLab Core memiliki standar workflow pengembangan
|
|
||||||
yang mencakup implementasi, pengujian, review, dokumentasi, dan proses
|
|
||||||
rilis. Workflow ini menjadi pedoman agar seluruh pengembangan tetap
|
|
||||||
konsisten, berkualitas, dan mudah dipelihara dalam jangka panjang.
|
|
||||||
@@ -1,249 +0,0 @@
|
|||||||
# RayLab Core Phase 9 --- Implementation Roadmap
|
|
||||||
|
|
||||||
## Tujuan
|
|
||||||
|
|
||||||
Fase 9 mendefinisikan urutan implementasi RayLab Core berdasarkan
|
|
||||||
prioritas, dependensi, dan nilai bisnis. Roadmap ini menjadi acuan resmi
|
|
||||||
agar pengembangan dilakukan secara bertahap dan terstruktur.
|
|
||||||
|
|
||||||
> Fokus fase ini adalah **urutan implementasi**, bukan rincian kode.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Prinsip Roadmap
|
|
||||||
|
|
||||||
- Bangun fondasi sebelum fitur.
|
|
||||||
- Implementasikan modul berdasarkan dependensi.
|
|
||||||
- Setiap fase menghasilkan sistem yang dapat dijalankan dan diuji.
|
|
||||||
- Hindari membangun fitur yang belum memiliki fondasi.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Tahap 1 --- Core Foundation
|
|
||||||
|
|
||||||
Tujuan:
|
|
||||||
|
|
||||||
- Bootstrap NestJS
|
|
||||||
- Konfigurasi Docker
|
|
||||||
- PostgreSQL
|
|
||||||
- Prisma
|
|
||||||
- Redis
|
|
||||||
- Logging
|
|
||||||
- Config Module
|
|
||||||
- Health Check
|
|
||||||
- Swagger
|
|
||||||
|
|
||||||
Target:
|
|
||||||
|
|
||||||
- Project dapat dijalankan.
|
|
||||||
- Health endpoint tersedia.
|
|
||||||
- Database dan Redis terhubung.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Tahap 2 --- Identity
|
|
||||||
|
|
||||||
Implementasi:
|
|
||||||
|
|
||||||
- User
|
|
||||||
- Profile
|
|
||||||
- Session
|
|
||||||
- User Repository
|
|
||||||
- User Service
|
|
||||||
- CRUD User
|
|
||||||
|
|
||||||
Target:
|
|
||||||
|
|
||||||
- Pengguna dapat dikelola melalui API.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Tahap 3 --- Authorization
|
|
||||||
|
|
||||||
Implementasi:
|
|
||||||
|
|
||||||
- Group
|
|
||||||
- Role
|
|
||||||
- Permission
|
|
||||||
- Domain Access
|
|
||||||
- Resource Access
|
|
||||||
- Special Access
|
|
||||||
|
|
||||||
Target:
|
|
||||||
|
|
||||||
- Model IAM RayLab Core berfungsi.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Tahap 4 --- Registry
|
|
||||||
|
|
||||||
Implementasi:
|
|
||||||
|
|
||||||
- Application Registry
|
|
||||||
- Service Registry
|
|
||||||
- Domain Registry
|
|
||||||
- Adapter Registry
|
|
||||||
|
|
||||||
Target:
|
|
||||||
|
|
||||||
- Seluruh aplikasi dan layanan dapat didaftarkan dan ditemukan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Tahap 5 --- Configuration
|
|
||||||
|
|
||||||
Implementasi:
|
|
||||||
|
|
||||||
- Runtime Settings
|
|
||||||
- Setting Categories
|
|
||||||
- Configuration API
|
|
||||||
|
|
||||||
Target:
|
|
||||||
|
|
||||||
- Konfigurasi dapat diubah tanpa mengubah kode aplikasi.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Tahap 6 --- Storage
|
|
||||||
|
|
||||||
Implementasi:
|
|
||||||
|
|
||||||
- Storage
|
|
||||||
- Volume
|
|
||||||
- Quota
|
|
||||||
- Path Mapping
|
|
||||||
|
|
||||||
Target:
|
|
||||||
|
|
||||||
- Penyimpanan logis dapat dikelola secara terpusat.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Tahap 7 --- Media Manager
|
|
||||||
|
|
||||||
Implementasi:
|
|
||||||
|
|
||||||
- Folder Template
|
|
||||||
- Folder Mapping
|
|
||||||
- Folder Permission
|
|
||||||
- Folder Provisioning
|
|
||||||
- Media Relocation
|
|
||||||
- Jellyfin Refresh Trigger
|
|
||||||
|
|
||||||
Target:
|
|
||||||
|
|
||||||
- Media Manager menjadi pengelola utama struktur media.
|
|
||||||
- Jellyfin hanya bertindak sebagai media server.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Tahap 8 --- Audit
|
|
||||||
|
|
||||||
Implementasi:
|
|
||||||
|
|
||||||
- Audit Log
|
|
||||||
- Audit Category
|
|
||||||
- Audit Target
|
|
||||||
|
|
||||||
Target:
|
|
||||||
|
|
||||||
- Seluruh aktivitas penting sistem tercatat.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Tahap 9 --- Dashboard
|
|
||||||
|
|
||||||
Implementasi:
|
|
||||||
|
|
||||||
- Dashboard API
|
|
||||||
- Statistik
|
|
||||||
- Ringkasan sistem
|
|
||||||
- Widget
|
|
||||||
|
|
||||||
Target:
|
|
||||||
|
|
||||||
- Dashboard dapat mengambil data dari seluruh modul.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Tahap 10 --- Health & Monitoring
|
|
||||||
|
|
||||||
Implementasi:
|
|
||||||
|
|
||||||
- Health Check
|
|
||||||
- Service Status
|
|
||||||
- Dependency Status
|
|
||||||
- Adapter Status
|
|
||||||
|
|
||||||
Target:
|
|
||||||
|
|
||||||
- Kondisi sistem dapat dipantau melalui API.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Tahap 11 --- External Adapter
|
|
||||||
|
|
||||||
Implementasi:
|
|
||||||
|
|
||||||
- Nextcloud Adapter
|
|
||||||
- Jellyfin Adapter
|
|
||||||
- Immich Adapter
|
|
||||||
- Adapter lain sesuai kebutuhan
|
|
||||||
|
|
||||||
Target:
|
|
||||||
|
|
||||||
- Integrasi eksternal menggunakan standar Adapter Layer.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Tahap 12 --- Optimization
|
|
||||||
|
|
||||||
Implementasi:
|
|
||||||
|
|
||||||
- Performance tuning
|
|
||||||
- Caching
|
|
||||||
- Query optimization
|
|
||||||
- Background job optimization
|
|
||||||
|
|
||||||
Target:
|
|
||||||
|
|
||||||
- Sistem siap untuk penggunaan jangka panjang.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Definition of Complete
|
|
||||||
|
|
||||||
Sebuah tahap dianggap selesai apabila:
|
|
||||||
|
|
||||||
- Seluruh fitur pada tahap tersebut selesai.
|
|
||||||
- Unit Test lulus.
|
|
||||||
- Integration Test lulus.
|
|
||||||
- Dokumentasi diperbarui.
|
|
||||||
- API terdokumentasi.
|
|
||||||
- Migration tersedia jika diperlukan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Deliverable
|
|
||||||
|
|
||||||
Pada akhir roadmap, RayLab Core memiliki:
|
|
||||||
|
|
||||||
- Fondasi teknis yang stabil.
|
|
||||||
- Identity & Authorization terintegrasi.
|
|
||||||
- Registry dan Configuration.
|
|
||||||
- Storage dan Media Manager.
|
|
||||||
- Audit.
|
|
||||||
- Dashboard API.
|
|
||||||
- Health Monitoring.
|
|
||||||
- Adapter Framework.
|
|
||||||
- Dokumentasi lengkap.
|
|
||||||
- Standar pengembangan yang konsisten.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Hasil Akhir Fase 9
|
|
||||||
|
|
||||||
Dokumen ini menjadi acuan resmi urutan implementasi RayLab Core sehingga
|
|
||||||
setiap modul dibangun berdasarkan fondasi yang telah tersedia,
|
|
||||||
mengurangi risiko perubahan besar di kemudian hari dan menjaga
|
|
||||||
konsistensi arsitektur selama pengembangan.
|
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
# RayLab Core Phase 10 --- Deployment & Operations
|
|
||||||
|
|
||||||
## Tujuan
|
|
||||||
|
|
||||||
Fase 10 mendefinisikan standar deployment dan operasional RayLab Core
|
|
||||||
agar sistem dapat berjalan stabil, aman, mudah dipelihara, serta siap
|
|
||||||
digunakan sebagai layanan 24/7 pada infrastruktur RayLab.
|
|
||||||
|
|
||||||
Dokumen ini menjadi acuan implementasi lingkungan produksi maupun
|
|
||||||
pengembangan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Prinsip Operasional
|
|
||||||
|
|
||||||
- Infrastructure as Code.
|
|
||||||
- Docker sebagai standar deployment.
|
|
||||||
- Zero manual configuration di production.
|
|
||||||
- Monitoring dan observability menjadi bagian dari sistem.
|
|
||||||
- Backup dan recovery harus dipersiapkan sejak awal.
|
|
||||||
- Deployment harus dapat diulang (repeatable).
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Environment
|
|
||||||
|
|
||||||
RayLab Core memiliki beberapa lingkungan:
|
|
||||||
|
|
||||||
- Development
|
|
||||||
- Testing
|
|
||||||
- Staging (opsional)
|
|
||||||
- Production
|
|
||||||
|
|
||||||
Setiap environment memiliki konfigurasi yang terpisah.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Container Architecture
|
|
||||||
|
|
||||||
Komponen utama:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
raylab-core
|
|
||||||
postgres-raylab
|
|
||||||
redis
|
|
||||||
```
|
|
||||||
|
|
||||||
Komponen pendukung:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
nginx-proxy-manager
|
|
||||||
cloudflared / gateway
|
|
||||||
homepage
|
|
||||||
uptime-kuma
|
|
||||||
```
|
|
||||||
|
|
||||||
Semua service berada pada Docker Network yang telah ditentukan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Deployment Workflow
|
|
||||||
|
|
||||||
``` text
|
|
||||||
Git
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Build
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Test
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Docker Image
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Deployment
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Health Check
|
|
||||||
```
|
|
||||||
|
|
||||||
Deployment baru dianggap berhasil apabila health check lulus.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Configuration Management
|
|
||||||
|
|
||||||
- Gunakan `.env` untuk konfigurasi.
|
|
||||||
- Secret tidak boleh di-hardcode.
|
|
||||||
- Konfigurasi dibaca melalui Config Module.
|
|
||||||
- File `.env.example` disediakan untuk dokumentasi.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
|
|
||||||
Semua log menggunakan format terstruktur.
|
|
||||||
|
|
||||||
Minimal mencatat:
|
|
||||||
|
|
||||||
- Timestamp
|
|
||||||
- Request ID
|
|
||||||
- Level
|
|
||||||
- Module
|
|
||||||
- Message
|
|
||||||
- Error (jika ada)
|
|
||||||
|
|
||||||
Log aplikasi dipisahkan dari log container.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Monitoring
|
|
||||||
|
|
||||||
Monitoring dilakukan menggunakan layanan RayLab:
|
|
||||||
|
|
||||||
- Uptime Kuma
|
|
||||||
- Grafana
|
|
||||||
- Prometheus
|
|
||||||
|
|
||||||
Metrik yang dipantau:
|
|
||||||
|
|
||||||
- CPU
|
|
||||||
- RAM
|
|
||||||
- Disk
|
|
||||||
- Response Time
|
|
||||||
- Error Rate
|
|
||||||
- Health Endpoint
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Health Check
|
|
||||||
|
|
||||||
Setiap service menyediakan endpoint:
|
|
||||||
|
|
||||||
``` text
|
|
||||||
GET /health
|
|
||||||
```
|
|
||||||
|
|
||||||
Health check minimal memverifikasi:
|
|
||||||
|
|
||||||
- Database
|
|
||||||
- Redis
|
|
||||||
- Internal Service
|
|
||||||
- Adapter penting
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Backup Strategy
|
|
||||||
|
|
||||||
Backup dilakukan secara berkala untuk:
|
|
||||||
|
|
||||||
- PostgreSQL
|
|
||||||
- Docker Volume
|
|
||||||
- Konfigurasi
|
|
||||||
- Dokumentasi
|
|
||||||
- Migration
|
|
||||||
|
|
||||||
Backup disimpan pada media penyimpanan terpisah sesuai strategi RayLab.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Disaster Recovery
|
|
||||||
|
|
||||||
Prosedur pemulihan minimal mencakup:
|
|
||||||
|
|
||||||
1. Restore database.
|
|
||||||
2. Restore konfigurasi.
|
|
||||||
3. Restore volume.
|
|
||||||
4. Jalankan container.
|
|
||||||
5. Verifikasi health check.
|
|
||||||
|
|
||||||
Targetnya adalah waktu pemulihan yang cepat dan terdokumentasi.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Security Baseline
|
|
||||||
|
|
||||||
Deployment harus memenuhi standar minimum:
|
|
||||||
|
|
||||||
- HTTPS
|
|
||||||
- Secret melalui environment
|
|
||||||
- Tidak mengekspos port yang tidak diperlukan
|
|
||||||
- Container berjalan dengan hak akses minimum jika memungkinkan
|
|
||||||
- Update dependency secara berkala
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Maintenance
|
|
||||||
|
|
||||||
Aktivitas rutin:
|
|
||||||
|
|
||||||
- Update dependency
|
|
||||||
- Update Docker image
|
|
||||||
- Review log
|
|
||||||
- Review backup
|
|
||||||
- Verifikasi monitoring
|
|
||||||
- Verifikasi dokumentasi
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Operational Checklist
|
|
||||||
|
|
||||||
Sebelum production:
|
|
||||||
|
|
||||||
- Docker berjalan normal.
|
|
||||||
- Health check lulus.
|
|
||||||
- Backup aktif.
|
|
||||||
- Monitoring aktif.
|
|
||||||
- Logging aktif.
|
|
||||||
- Dokumentasi diperbarui.
|
|
||||||
- Migration terbaru diterapkan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Hasil Akhir Fase 10
|
|
||||||
|
|
||||||
Pada akhir fase ini RayLab Core memiliki standar deployment dan
|
|
||||||
operasional yang konsisten untuk lingkungan pengembangan maupun
|
|
||||||
produksi. Seluruh proses deployment, monitoring, backup, logging, dan
|
|
||||||
pemeliharaan terdokumentasi sehingga sistem siap dijalankan sebagai
|
|
||||||
layanan inti ekosistem RayLab.
|
|
||||||
@@ -1,205 +0,0 @@
|
|||||||
# RayLab Core Phase 11 --- Scaling Strategy
|
|
||||||
|
|
||||||
## Tujuan
|
|
||||||
|
|
||||||
Fase 11 mendefinisikan strategi pengembangan jangka panjang RayLab Core
|
|
||||||
agar mampu berkembang dari sistem yang melayani kebutuhan pribadi
|
|
||||||
menjadi platform yang dapat menangani lebih banyak aplikasi, pengguna,
|
|
||||||
layanan, dan integrasi tanpa perlu mengubah arsitektur dasarnya.
|
|
||||||
|
|
||||||
Dokumen ini menjadi acuan untuk menjaga skalabilitas teknis maupun
|
|
||||||
organisasi proyek.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Prinsip Skalabilitas
|
|
||||||
|
|
||||||
- Bangun untuk kebutuhan saat ini, rancang untuk kebutuhan masa depan.
|
|
||||||
- Modular sebelum distributed.
|
|
||||||
- Scale up terlebih dahulu, scale out bila diperlukan.
|
|
||||||
- Hindari premature optimization.
|
|
||||||
- Pertahankan kompatibilitas ke belakang (backward compatibility) jika
|
|
||||||
memungkinkan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Tahapan Skalabilitas
|
|
||||||
|
|
||||||
## Tahap 1 --- Single Server
|
|
||||||
|
|
||||||
Karakteristik:
|
|
||||||
|
|
||||||
- Satu instance RayLab Core.
|
|
||||||
- Satu PostgreSQL.
|
|
||||||
- Satu Redis.
|
|
||||||
- Docker Compose.
|
|
||||||
|
|
||||||
Cocok untuk homelab dan pengembangan awal.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Tahap 2 --- Multi Service
|
|
||||||
|
|
||||||
Mulai memisahkan layanan pendukung.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
- Database terpisah.
|
|
||||||
- Monitoring terpisah.
|
|
||||||
- Backup terpisah.
|
|
||||||
- Worker background.
|
|
||||||
|
|
||||||
Tujuan:
|
|
||||||
|
|
||||||
Mengurangi coupling antar layanan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## Tahap 3 --- Horizontal Scaling
|
|
||||||
|
|
||||||
Jika beban meningkat:
|
|
||||||
|
|
||||||
- Menjalankan beberapa instance RayLab Core.
|
|
||||||
- Menggunakan reverse proxy atau load balancer.
|
|
||||||
- Session dipindahkan ke Redis.
|
|
||||||
- Stateless API menjadi syarat utama.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Modular Growth
|
|
||||||
|
|
||||||
Modul baru harus dapat ditambahkan tanpa mengubah modul yang sudah ada.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
- AI
|
|
||||||
- Notification
|
|
||||||
- Billing
|
|
||||||
- Home Automation
|
|
||||||
- Marketplace Adapter
|
|
||||||
|
|
||||||
Integrasi dilakukan melalui service dan adapter yang sudah ada.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Adapter Expansion
|
|
||||||
|
|
||||||
Seluruh integrasi eksternal mengikuti pola Adapter.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
- Nextcloud
|
|
||||||
- Jellyfin
|
|
||||||
- Immich
|
|
||||||
- Gitea
|
|
||||||
- Forgejo
|
|
||||||
- Home Assistant
|
|
||||||
- Ollama
|
|
||||||
- Future services
|
|
||||||
|
|
||||||
Tidak ada business logic di dalam adapter.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Database Scaling
|
|
||||||
|
|
||||||
Strategi bertahap:
|
|
||||||
|
|
||||||
1. Optimasi query.
|
|
||||||
2. Indexing.
|
|
||||||
3. Connection pooling.
|
|
||||||
4. Read replica (jika diperlukan).
|
|
||||||
5. Sharding hanya jika benar-benar dibutuhkan.
|
|
||||||
|
|
||||||
Perubahan dilakukan bertahap berdasarkan kebutuhan nyata.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Storage Scaling
|
|
||||||
|
|
||||||
Storage harus mendukung penambahan kapasitas tanpa mengubah business
|
|
||||||
logic.
|
|
||||||
|
|
||||||
Contoh:
|
|
||||||
|
|
||||||
- HDD tambahan.
|
|
||||||
- NAS.
|
|
||||||
- Object Storage (masa depan).
|
|
||||||
|
|
||||||
Storage Layer menjadi abstraksi terhadap media penyimpanan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# API Evolution
|
|
||||||
|
|
||||||
Strategi pengembangan API:
|
|
||||||
|
|
||||||
- Gunakan versioning.
|
|
||||||
- Hindari breaking changes.
|
|
||||||
- Tandai endpoint lama sebagai deprecated.
|
|
||||||
- Dokumentasikan seluruh perubahan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Performance Strategy
|
|
||||||
|
|
||||||
Fokus optimasi:
|
|
||||||
|
|
||||||
- Query database.
|
|
||||||
- Caching Redis.
|
|
||||||
- Background Job.
|
|
||||||
- Lazy Loading.
|
|
||||||
- Pagination.
|
|
||||||
- Batch Processing.
|
|
||||||
|
|
||||||
Optimasi dilakukan berdasarkan hasil monitoring.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Observability
|
|
||||||
|
|
||||||
Seiring pertumbuhan sistem:
|
|
||||||
|
|
||||||
- Logging terstruktur.
|
|
||||||
- Metrics.
|
|
||||||
- Health Check.
|
|
||||||
- Audit.
|
|
||||||
- Distributed tracing (jika diperlukan).
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Maintainability
|
|
||||||
|
|
||||||
Setiap modul harus:
|
|
||||||
|
|
||||||
- Memiliki dokumentasi.
|
|
||||||
- Memiliki test.
|
|
||||||
- Mengikuti coding convention.
|
|
||||||
- Mengikuti layered architecture.
|
|
||||||
|
|
||||||
Refactoring dilakukan secara bertahap tanpa mengubah kontrak publik.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Long-Term Vision
|
|
||||||
|
|
||||||
RayLab Core dirancang agar dapat berkembang menjadi platform yang
|
|
||||||
mendukung:
|
|
||||||
|
|
||||||
- Banyak aplikasi.
|
|
||||||
- Banyak pengguna.
|
|
||||||
- Banyak domain.
|
|
||||||
- Banyak adapter.
|
|
||||||
- Banyak layanan.
|
|
||||||
|
|
||||||
Dengan tetap mempertahankan satu pusat Identity & Access Management dan
|
|
||||||
satu standar arsitektur.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Hasil Akhir Fase 11
|
|
||||||
|
|
||||||
Pada akhir fase ini, RayLab Core memiliki strategi skalabilitas yang
|
|
||||||
jelas untuk pertumbuhan jangka panjang. Arsitektur yang telah dibangun
|
|
||||||
pada fase-fase sebelumnya dapat diperluas tanpa perubahan mendasar
|
|
||||||
sehingga sistem tetap modular, konsisten, dan mudah dipelihara.
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
# RayLab Core Phase 12 --- Future Vision & Evolution
|
|
||||||
|
|
||||||
## Tujuan
|
|
||||||
|
|
||||||
Fase 12 mendokumentasikan visi jangka panjang RayLab Core sebagai
|
|
||||||
platform inti ekosistem RayLab. Dokumen ini menjadi panduan arah
|
|
||||||
pengembangan di masa depan tanpa mengubah prinsip arsitektur yang telah
|
|
||||||
ditetapkan.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Visi
|
|
||||||
|
|
||||||
RayLab Core dirancang sebagai pusat pengelolaan identitas, otorisasi,
|
|
||||||
konfigurasi, penyimpanan, media, dan integrasi untuk seluruh layanan
|
|
||||||
dalam ekosistem RayLab.
|
|
||||||
|
|
||||||
Prinsip utamanya adalah membangun platform yang modular, dapat
|
|
||||||
diperluas, terdokumentasi, dan mudah dipelihara.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Sasaran Jangka Panjang
|
|
||||||
|
|
||||||
- Menjadi pusat Identity & Access Management (IAM).
|
|
||||||
- Menjadi registry seluruh aplikasi RayLab.
|
|
||||||
- Menjadi pusat konfigurasi layanan.
|
|
||||||
- Menjadi pengelola storage dan media.
|
|
||||||
- Menjadi fondasi integrasi seluruh aplikasi internal.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Evolusi Modul
|
|
||||||
|
|
||||||
Modul yang dapat ditambahkan di masa depan antara lain:
|
|
||||||
|
|
||||||
- Notification Center
|
|
||||||
- Workflow Engine
|
|
||||||
- Scheduler
|
|
||||||
- AI Integration
|
|
||||||
- Home Automation
|
|
||||||
- Billing
|
|
||||||
- Analytics
|
|
||||||
- Search Service
|
|
||||||
|
|
||||||
Modul baru harus mengikuti standar Layered Architecture dan tidak
|
|
||||||
mengubah kontrak modul yang sudah ada.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Evolusi Adapter
|
|
||||||
|
|
||||||
Adapter baru dapat ditambahkan untuk layanan seperti:
|
|
||||||
|
|
||||||
- Home Assistant
|
|
||||||
- Ollama
|
|
||||||
- Forgejo
|
|
||||||
- GitHub
|
|
||||||
- S3 Compatible Storage
|
|
||||||
- MQTT Broker
|
|
||||||
- Sistem pihak ketiga lainnya
|
|
||||||
|
|
||||||
Seluruh integrasi dilakukan melalui Adapter Layer agar business logic
|
|
||||||
tetap independen.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Ekosistem RayLab
|
|
||||||
|
|
||||||
RayLab Core diharapkan menjadi fondasi bagi berbagai aplikasi, seperti:
|
|
||||||
|
|
||||||
- Dashboard
|
|
||||||
- Warung Management
|
|
||||||
- File Management
|
|
||||||
- Media Management
|
|
||||||
- Monitoring
|
|
||||||
- Wiki
|
|
||||||
- Mobile Applications
|
|
||||||
- Future Services
|
|
||||||
|
|
||||||
Semua aplikasi menggunakan standar autentikasi dan otorisasi yang sama.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Standar Evolusi
|
|
||||||
|
|
||||||
Perubahan besar harus:
|
|
||||||
|
|
||||||
- Didokumentasikan melalui Architecture Decision Record (ADR).
|
|
||||||
- Menjaga kompatibilitas jika memungkinkan.
|
|
||||||
- Memiliki migration path yang jelas.
|
|
||||||
- Disertai pembaruan dokumentasi dan pengujian.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Continuous Improvement
|
|
||||||
|
|
||||||
Pengembangan dilakukan secara berkelanjutan melalui:
|
|
||||||
|
|
||||||
- Refactoring berkala.
|
|
||||||
- Peningkatan performa.
|
|
||||||
- Evaluasi keamanan.
|
|
||||||
- Penyempurnaan dokumentasi.
|
|
||||||
- Otomatisasi deployment dan pengujian.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Success Criteria
|
|
||||||
|
|
||||||
RayLab Core dianggap berhasil apabila:
|
|
||||||
|
|
||||||
- Mudah dikembangkan.
|
|
||||||
- Mudah diintegrasikan.
|
|
||||||
- Mudah dipelihara.
|
|
||||||
- Memiliki dokumentasi yang lengkap.
|
|
||||||
- Memiliki kualitas kode yang konsisten.
|
|
||||||
- Menjadi fondasi yang stabil bagi seluruh aplikasi RayLab.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Penutup
|
|
||||||
|
|
||||||
Fase ini tidak mendefinisikan fitur baru, melainkan arah pengembangan
|
|
||||||
jangka panjang. Setiap evolusi RayLab Core harus tetap berpegang pada
|
|
||||||
prinsip modularitas, konsistensi, keamanan, dokumentasi, dan
|
|
||||||
maintainability yang telah ditetapkan sejak fase awal.
|
|
||||||
|
|
||||||
Dengan demikian, RayLab Core dapat berkembang seiring kebutuhan tanpa
|
|
||||||
kehilangan stabilitas arsitekturnya.
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
Ringkasan
|
|
||||||
|
|
||||||
Dokumen ini menjelaskan struktur folder proyek, alur kerja (workflow) pengembangan, dan bagaimana data diproses dari UI sampai ke penyimpanan (data flow). Tujuan: memberi panduan onboarding cepat untuk developer agar konsisten dengan arsitektur RayLab Core.
|
|
||||||
|
|
||||||
1. Struktur Folder (penjelasan)
|
|
||||||
|
|
||||||
```text Docs/Structure-Workflow-DataFlow.md
|
|
||||||
raylab-core/
|
|
||||||
├── docker/ # Docker compose, konfigurasi container
|
|
||||||
├── prisma/ # Prisma schema, migrations, seed
|
|
||||||
├── docs/ # Dokumentasi proyek (arsitektur, ADR, panduan)
|
|
||||||
├── scripts/ # Script helper (migrate, seed, build)
|
|
||||||
├── src/ # Source code aplikasi
|
|
||||||
│ ├── main.ts # Bootstrap aplikasi
|
|
||||||
│ ├── app.module.ts # Root module (dependensi utama)
|
|
||||||
│ ├── common/ # Guards, filters, interceptors, middleware, logger, utils
|
|
||||||
│ ├── config/ # ConfigModule, ConfigService (env parsing)
|
|
||||||
│ ├── modules/ # Domain modules (identity, authorization, media, ...)
|
|
||||||
│ │ ├── identity/ # Example: controllers/, services/, repositories/, dto/, entities/
|
|
||||||
│ │ └── ...
|
|
||||||
│ ├── adapters/ # External integrations (Nextcloud, Jellyfin, Immich)
|
|
||||||
│ └── shared/ # Shared services (PrismaService, event bus, constants)
|
|
||||||
├── tests/ # Unit, integration, e2e
|
|
||||||
├── .env.example
|
|
||||||
├── docker-compose.yml
|
|
||||||
└── README.md
|
|
||||||
```
|
|
||||||
|
|
||||||
Penjelasan singkat folder penting:
|
|
||||||
- src/modules: setiap module merepresentasikan domain bisnis. Contoh: identity, authorization, registry. Module berisi subfolder terstruktur: controllers, services, domain, repositories, dto, entities, interfaces, events.
|
|
||||||
- src/common: komponen yang dipakai lintas module, seperti exception filters, authentication guards, middleware request-id, logging interceptor.
|
|
||||||
- src/adapters: semua integrasi eksternal hanya lewat adapter (no business logic). Adapter expose interface yang dipakai application service.
|
|
||||||
- src/shared: service bersama seperti PrismaService (DB client), event bus, constant classes.
|
|
||||||
- prisma/: schema prisma, migrations — migration menjadi sumber kebenaran struktur DB.
|
|
||||||
|
|
||||||
2. Development Workflow (Ringkas)
|
|
||||||
|
|
||||||
- Branching:
|
|
||||||
- main: selalu stabil dan dapat direlease
|
|
||||||
- feature/*, fix/*, refactor/* untuk pekerjaan terpisah
|
|
||||||
- Commit convention: type(scope): description (feat, fix, docs, test, refactor)
|
|
||||||
- PR checklist: build ok, unit tests ok, integration tests ok, dokumentasi diupdate, migration tersedia jika perlu
|
|
||||||
- CI Pipeline: lint → test → build → prisma migrate (dry-run) → docker build
|
|
||||||
- Release: tag versi (semver), build docker image, deploy via docker compose / orchestrator
|
|
||||||
|
|
||||||
3. Request & Data Flow — dari UI sampai Database
|
|
||||||
|
|
||||||
Langkah umum (singkat):
|
|
||||||
|
|
||||||
UI (web/mobile/cli)
|
|
||||||
↓ (HTTP request JSON + headers)
|
|
||||||
API Gateway / Reverse Proxy (optional)
|
|
||||||
↓
|
|
||||||
NestJS Controller (src/modules/.../controllers)
|
|
||||||
- Validasi ringan (DTO + Pipes)
|
|
||||||
- Mapping request → DTO
|
|
||||||
- Menambahkan metadata (requestId, auth user)
|
|
||||||
↓
|
|
||||||
Application Service (src/modules/.../services)
|
|
||||||
- Implementasi use-case / orchestration
|
|
||||||
- Koordinasi Domain Services, Repositories, Adapters
|
|
||||||
- Menjalankan transaksi jika perlu
|
|
||||||
- Men-trigger audit/event/background job
|
|
||||||
↓
|
|
||||||
Domain Service / Domain Logic (src/modules/.../domain atau services)
|
|
||||||
- Semua aturan bisnis utama ditempatkan di sini
|
|
||||||
- Validasi domain, rules, invariants
|
|
||||||
↓
|
|
||||||
Repository (src/modules/.../repositories)
|
|
||||||
- Akses database (Prisma client) — Save, Find, Update, Delete
|
|
||||||
- Tidak ada business logic berat di sini
|
|
||||||
↓
|
|
||||||
Database (PostgreSQL)
|
|
||||||
|
|
||||||
Respon balik mengikuti format standar API (success/error)
|
|
||||||
|
|
||||||
Contoh alur lebih rinci (Create User):
|
|
||||||
1. UI POST /api/v1/users {email, displayName, password}
|
|
||||||
2. Controller menerima request, Pipe class-validator validasi DTO
|
|
||||||
3. Controller memanggil UserService.create(createUserDto, ctx)
|
|
||||||
4. UserService melakukan business logic: cek duplikasi email (via UserRepository), hash password (di service), mempersiapkan data user
|
|
||||||
5. UserService memanggil UserRepository.create(data)
|
|
||||||
6. Repository menggunakan PrismaClient untuk menyimpan ke tabel identity.users
|
|
||||||
7. Setelah sukses, UserService men-trigger AuditService.log(...) dan mengembalikan DTO respon
|
|
||||||
8. Controller membungkus respon ke format standar dan mengembalikan ke client
|
|
||||||
|
|
||||||
4. Non-blocking / Side Effects
|
|
||||||
|
|
||||||
- Audit: dipicu oleh Application Service setelah perubahan penting; bisa synchronous atau pushed ke queue
|
|
||||||
- Events: domain events dapat dipublish ke event bus (in-memory atau Redis) untuk worker/background processing
|
|
||||||
- Background Jobs: proses berat (email, refresh library, thumbnail generation) diproses oleh worker (BullMQ/Redis)
|
|
||||||
- Cache: gunakan Redis untuk session, rate-limiting, cache query hasil expensive
|
|
||||||
|
|
||||||
5. Error Handling & Observability
|
|
||||||
|
|
||||||
- Request ID: middleware menambahkan X-Request-ID jika belum ada; dipakai di log dan audit
|
|
||||||
- Structured logging: Pino dengan fields {timestamp, level, module, requestId, message}
|
|
||||||
- Error response standard:
|
|
||||||
|
|
||||||
```json Docs/Structure-Workflow-DataFlow.md
|
|
||||||
{
|
|
||||||
"success": false,
|
|
||||||
"error": { "code": "USER_NOT_FOUND", "message": "User not found" }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- Success response standard:
|
|
||||||
|
|
||||||
```json Docs/Structure-Workflow-DataFlow.md
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"data": {},
|
|
||||||
"meta": { "requestId": "...", "timestamp": "..." }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- Monitoring: expose /health yang memeriksa koneksi DB, Redis, adapter penting. Integrasikan metrics untuk Prometheus/Grafana.
|
|
||||||
|
|
||||||
6. Best Practices & Rules
|
|
||||||
|
|
||||||
- Controller tipis: hanya validasi + mapping + panggil service
|
|
||||||
- Business logic hanya di Domain/Service
|
|
||||||
- Repository hanya query DB (Prisma)
|
|
||||||
- Adapter untuk komunikasi eksternal tanpa business logic
|
|
||||||
- Semua perubahan DB melalui migration (Prisma Migrate)
|
|
||||||
- Gunakan DTO + class-validator untuk input validation
|
|
||||||
- Gunakan UUID v7 untuk primary key, timestamps created_at/updated_at/deleted_at
|
|
||||||
- Soft delete: gunakan deleted_at
|
|
||||||
- Semua endpoint harus terdokumentasi di Swagger/OpenAPI
|
|
||||||
|
|
||||||
7. Contoh file lokasi logis untuk use-case "Create User"
|
|
||||||
|
|
||||||
- src/modules/identity/controllers/users.controller.ts (HTTP layer)
|
|
||||||
- src/modules/identity/dto/create-user.dto.ts (validation)
|
|
||||||
- src/modules/identity/services/user.service.ts (application service)
|
|
||||||
- src/modules/identity/domain/user.domain.ts (domain rules)
|
|
||||||
- src/modules/identity/repositories/user.repository.ts (prisma queries)
|
|
||||||
- src/shared/prisma.service.ts (prisma client)
|
|
||||||
- src/common/middleware/request-id.middleware.ts (request id)
|
|
||||||
- src/modules/audit/services/audit.service.ts (audit logging)
|
|
||||||
|
|
||||||
Penutup
|
|
||||||
|
|
||||||
Dokumen ini menjadi ringkasan teknis untuk struktur kode dan alur data. Jika Anda ingin, saya bisa:
|
|
||||||
- Menggenerate file contoh (controller/service/repository/dto) untuk use-case Create User sesuai struktur di atas.
|
|
||||||
- Membuat diagram sequence (plantuml) untuk alur request.
|
|
||||||
- Menambahkan template middleware, audit service, dan contoh Prisma model untuk User.
|
|
||||||
|
|
||||||
Pilih aksi selanjutnya jika ingin saya buatkan implementasi contoh secara otomatis.
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
// REMOVED: file content deleted per user request
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
# placeholder docker-compose (to be filled later)
|
|
||||||
version: '3.8'
|
version: '3.8'
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
|
|||||||
Generated
+5432
File diff suppressed because it is too large
Load Diff
+43
-29
@@ -2,55 +2,69 @@
|
|||||||
"name": "raylab-core",
|
"name": "raylab-core",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "RayLab Core - Identity & Platform services",
|
"description": "RayLab Core - Identity & Platform Services",
|
||||||
"author": "RayLab",
|
"author": "RayLab",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=20.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node dist/main.js",
|
"start": "node dist/main.js",
|
||||||
"start:dev": "ts-node-dev --respawn --pretty --transpile-only src/main.ts",
|
"start:dev": "ts-node-dev --respawn --pretty --transpile-only src/main.ts",
|
||||||
"build": "tsc -p tsconfig.json",
|
"build": "tsc -p tsconfig.json",
|
||||||
"lint": "eslint . --ext .ts",
|
"lint": "eslint \"src/**/*.ts\"",
|
||||||
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"",
|
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
"prisma:generate": "prisma generate",
|
"prisma:generate": "prisma generate",
|
||||||
"prisma:migrate": "prisma migrate dev",
|
"prisma:migrate": "prisma migrate dev",
|
||||||
|
"prisma:deploy": "prisma migrate deploy",
|
||||||
|
"prisma:reset": "prisma migrate reset",
|
||||||
"prisma:studio": "prisma studio",
|
"prisma:studio": "prisma studio",
|
||||||
"prisma:seed": "ts-node prisma/seed.ts"
|
"prisma:seed": "ts-node prisma/seed.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/common": "^10.0.0",
|
"@nestjs/common": "^10.4.20",
|
||||||
"@nestjs/core": "^10.0.0",
|
"@nestjs/config": "^3.3.0",
|
||||||
"@nestjs/platform-express": "^10.0.0",
|
"@nestjs/core": "^10.4.20",
|
||||||
"@nestjs/config": "^3.0.0",
|
"@nestjs/jwt": "^10.2.0",
|
||||||
"@nestjs/swagger": "^6.0.0",
|
"@nestjs/mapped-types": "^2.1.0",
|
||||||
|
"@nestjs/passport": "^10.0.3",
|
||||||
|
"@nestjs/platform-express": "^10.4.20",
|
||||||
|
"@nestjs/swagger": "^7.4.2",
|
||||||
|
"@prisma/client": "^5.22.0",
|
||||||
|
"bcrypt": "^5.1.1",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.0",
|
"class-validator": "^0.14.2",
|
||||||
"reflect-metadata": "^0.1.13",
|
"dotenv": "^16.6.1",
|
||||||
"rxjs": "^7.8.0",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"swagger-ui-express": "^4.6.3",
|
"passport": "^0.7.0",
|
||||||
"@prisma/client": "^5.0.0",
|
"passport-jwt": "^4.0.1",
|
||||||
"pino": "^8.0.0",
|
"pino": "^9.7.0",
|
||||||
"dotenv": "^16.0.0"
|
"reflect-metadata": "^0.2.2",
|
||||||
|
"rxjs": "^7.8.2",
|
||||||
|
"swagger-ui-express": "^5.0.1",
|
||||||
|
"uuid": "^11.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.0.0",
|
"@types/bcrypt": "^5.0.2",
|
||||||
"@types/swagger-ui-express": "^4.1.4",
|
"@types/jest": "^29.5.14",
|
||||||
"typescript": "^5.1.0",
|
"@types/node": "^22.13.10",
|
||||||
"ts-node": "^10.9.1",
|
"@types/passport-jwt": "^4.0.1",
|
||||||
|
"@types/supertest": "^6.0.3",
|
||||||
|
"@types/swagger-ui-express": "^4.1.8",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8.25.0",
|
||||||
|
"@typescript-eslint/parser": "^8.25.0",
|
||||||
|
"eslint": "^9.21.0",
|
||||||
|
"@eslint/js": "^9.21.0",
|
||||||
|
"globals": "^16.0.0",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"prettier": "^3.5.3",
|
||||||
|
"prisma": "^5.22.0",
|
||||||
|
"supertest": "^7.0.0",
|
||||||
|
"ts-jest": "^29.2.6",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
"ts-node-dev": "^2.0.0",
|
"ts-node-dev": "^2.0.0",
|
||||||
"prisma": "^5.0.0",
|
"typescript": "^5.8.2"
|
||||||
"eslint": "^8.0.0",
|
|
||||||
"@typescript-eslint/parser": "^6.0.0",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
||||||
"prettier": "^2.8.0",
|
|
||||||
"jest": "^29.0.0",
|
|
||||||
"ts-jest": "^29.0.0",
|
|
||||||
"@types/jest": "^29.0.0",
|
|
||||||
"supertest": "^6.3.0",
|
|
||||||
"@types/supertest": "^2.0.12"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-2
@@ -1,5 +1,6 @@
|
|||||||
// Prisma schema placeholder
|
// Prisma schema
|
||||||
// Define models in implementation phase
|
// Basic User model for RayLab Core
|
||||||
|
|
||||||
generator client {
|
generator client {
|
||||||
provider = "prisma-client-js"
|
provider = "prisma-client-js"
|
||||||
}
|
}
|
||||||
@@ -8,3 +9,15 @@ datasource db {
|
|||||||
provider = "postgresql"
|
provider = "postgresql"
|
||||||
url = env("DATABASE_URL")
|
url = env("DATABASE_URL")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model User {
|
||||||
|
id String @id @default(uuid())
|
||||||
|
name String
|
||||||
|
email String @unique
|
||||||
|
password String
|
||||||
|
status String @default("ACTIVE")
|
||||||
|
metadata Json?
|
||||||
|
created_at DateTime @default(now())
|
||||||
|
updated_at DateTime @updatedAt
|
||||||
|
deleted_at DateTime?
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
src/ (overview)
|
||||||
|
|
||||||
|
Penjelasan singkat:
|
||||||
|
Folder src adalah root source code aplikasi. Berikut child-folder utama yang ada saat ini:
|
||||||
|
- core/: framework-level utilities (events, middleware, logger, exceptions, queue)
|
||||||
|
- shared/: shared utilities, types, decorators
|
||||||
|
- config/: configuration module dan service
|
||||||
|
- adapters/: external integrations
|
||||||
|
- modules/: domain modules (identity, authorization, media, storage, ...)
|
||||||
|
|
||||||
|
Contoh file entrypoint:
|
||||||
|
- src/main.ts: bootstrap aplikasi, register global middleware, setup swagger
|
||||||
|
- src/app.module.ts: root module yang mengimpor module lainnya
|
||||||
|
|
||||||
|
Panduan singkat:
|
||||||
|
- Ikuti struktur Clean Architecture untuk modul-modul dalam modules/.
|
||||||
|
- Jangan menaruh business logic di shared/ atau core/.
|
||||||
|
- Gunakan event-driven dan queue untuk proses lintas module.
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
adapters/
|
||||||
|
|
||||||
|
Penjelasan singkat:
|
||||||
|
Folder adapters berisi integrasi ke layanan eksternal. Adapter bertanggung jawab sebagai "translator" antara API eksternal dan interface aplikasi.
|
||||||
|
|
||||||
|
Struktur contoh:
|
||||||
|
- adapters/identity/
|
||||||
|
- adapters/media/ (jellyfin, immich)
|
||||||
|
- adapters/storage/ (filesystem, nextcloud, s3)
|
||||||
|
- adapters/notification/ (email, webhook)
|
||||||
|
- adapters/integration/ (3rd-party integrations)
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Adapter tidak mengandung business logic. Business logic tetap berada di Application/Domain layer.
|
||||||
|
- Adapter harus mengimplementasikan interface yang didefinisikan di module/domain yang memerlukannya.
|
||||||
|
|
||||||
|
Contoh file:
|
||||||
|
- src/adapters/media/jellyfin/jellyfin.adapter.ts
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
adapters/identity/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Adapter untuk layanan identity eksternal (jika ada). Implementasi translation antara eksternal API dan internal interface.
|
||||||
|
|
||||||
|
Contoh file:
|
||||||
|
- identity.adapter.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Jangan letakkan business logic di adapter.
|
||||||
|
- Adapter harus mengimplementasikan interface yang dibutuhkan module.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
adapters/immich/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Adapter spesifik untuk Immich integration.
|
||||||
|
|
||||||
|
Contoh file:
|
||||||
|
- immich.adapter.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Adapter harus stateless dan dapat digunakan kembali.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
adapters/integration/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Adapter untuk integrasi third-party yang lebih spesifik (mis. external API vendors).
|
||||||
|
|
||||||
|
Contoh file:
|
||||||
|
- vendor-a.adapter.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Jangan masukkan business rules di sini; hanya mapping dan komunikasi.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
adapters/jellyfin/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Adapter spesifik untuk Jellyfin (trigger library refresh, fetch metadata).
|
||||||
|
|
||||||
|
Contoh file:
|
||||||
|
- jellyfin.adapter.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Jangan masukkan orchestration di adapter; gunakan service/module yang memanggil adapter.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
adapters/media/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Adapters untuk media services seperti Jellyfin atau Immich.
|
||||||
|
|
||||||
|
Contoh file:
|
||||||
|
- jellyfin.adapter.ts
|
||||||
|
- immich.adapter.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Adapter hanya wrap API calls dan mapping data.
|
||||||
|
- Integrasi heavy operations diserahkan ke queue/worker.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
adapters/nextcloud/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Adapter untuk Nextcloud (file ops, permissions).
|
||||||
|
|
||||||
|
Contoh file:
|
||||||
|
- nextcloud.adapter.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Pastikan error mapping agar client mendapatkan pesan yang konsisten.
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
adapters/notification/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Adapter untuk sistem notifikasi (email, webhook).
|
||||||
|
|
||||||
|
Contoh file:
|
||||||
|
- email.adapter.ts
|
||||||
|
- webhook.adapter.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Notification adapter bersifat best-effort; retry dan persistence dikelola oleh queue/worker.
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
adapters/storage/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Adapters untuk storage backends: filesystem, nextcloud, s3, minio.
|
||||||
|
|
||||||
|
Contoh file:
|
||||||
|
- filesystem.adapter.ts
|
||||||
|
- nextcloud.adapter.ts
|
||||||
|
- s3.adapter.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Adapter expose interface seperti readFile, writeFile, listFolder.
|
||||||
|
- Business logic integrasi tetap berada di module/storage.
|
||||||
+15
-1
@@ -1 +1,15 @@
|
|||||||
// placeholder app.module.ts
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
|
||||||
|
import { IdentityModule } from './modules/identity/identity.module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
ConfigModule.forRoot({
|
||||||
|
isGlobal: true,
|
||||||
|
}),
|
||||||
|
|
||||||
|
IdentityModule,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AppModule {}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// MOVED: see src/core and src/shared
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// MOVED: see src/core/guards or module-specific guards
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// MOVED: see src/core/exceptions
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// MOVED: see src/core/guards or module-specific guards
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// MOVED: see src/core/interceptors or module-specific interceptors
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// MOVED: see src/core/logger
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// MOVED: see src/core/middleware
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// MOVED: see src/shared/utils
|
||||||
@@ -1 +0,0 @@
|
|||||||
// placeholder config.module.ts
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
// placeholder config.service.ts
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
core/
|
||||||
|
|
||||||
|
Penjelasan singkat:
|
||||||
|
Folder core berisi fasilitas framework-level yang dipakai lintas modul. Jangan tempatkan business logic domain di sini. Core menyediakan building block seperti:
|
||||||
|
|
||||||
|
- Event bus / dispatcher
|
||||||
|
- Global middleware (request-id, correlation id)
|
||||||
|
- Global exception filters
|
||||||
|
- Logger (pino wrapper)
|
||||||
|
- Queue foundation (struktur jobs/workers/processors)
|
||||||
|
|
||||||
|
Contoh file yang ada di folder ini:
|
||||||
|
- src/core/events/event.interface.ts
|
||||||
|
- src/core/events/event-publisher.ts
|
||||||
|
- src/core/logger/logger.service.ts
|
||||||
|
- src/core/middleware/request-id.middleware.ts
|
||||||
|
- src/core/exceptions/http-exception.filter.ts
|
||||||
|
- src/core/queue/*
|
||||||
|
|
||||||
|
Panduan singkat:
|
||||||
|
- Semua event domain didefinisikan di core/events. Module boleh publish/subscribe.
|
||||||
|
- Logger tersedia sebagai utilitas global (injectable or exported instance).
|
||||||
|
- Queue hanya menyediakan pondasi; integrasi spesifik (BullMQ, Redis) dilakukan pada fase infrastructure atau worker.
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import {
|
||||||
|
Injectable,
|
||||||
|
CanActivate,
|
||||||
|
ExecutionContext,
|
||||||
|
UnauthorizedException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { JwtService } from '@nestjs/jwt';
|
||||||
|
import { Request } from 'express';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class JwtAuthGuard implements CanActivate {
|
||||||
|
constructor(
|
||||||
|
private readonly jwtService: JwtService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||||
|
const request = context.switchToHttp().getRequest<Request>();
|
||||||
|
|
||||||
|
const authHeader = request.headers.authorization;
|
||||||
|
|
||||||
|
if (!authHeader) {
|
||||||
|
throw new UnauthorizedException('Authorization header is missing.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const [type, token] = authHeader.split(' ');
|
||||||
|
|
||||||
|
if (type !== 'Bearer' || !token) {
|
||||||
|
throw new UnauthorizedException('Invalid authorization header.');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payload = await this.jwtService.verifyAsync(token);
|
||||||
|
|
||||||
|
request['user'] = payload;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
throw new UnauthorizedException('Invalid or expired token.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { Request } from 'express';
|
||||||
|
|
||||||
|
export interface JwtPayload {
|
||||||
|
sub: string;
|
||||||
|
email: string;
|
||||||
|
role: string;
|
||||||
|
permissions: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuthenticatedRequest extends Request {
|
||||||
|
user: JwtPayload;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export default () => ({
|
||||||
|
database: {
|
||||||
|
url:
|
||||||
|
`postgresql://` +
|
||||||
|
`${process.env.DB_USER}:` +
|
||||||
|
`${process.env.DB_PASSWORD}@` +
|
||||||
|
`${process.env.SERVER_HOST}:` +
|
||||||
|
`${process.env.DB_PORT}/` +
|
||||||
|
`${process.env.DB_NAME}?schema=public`
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
core/events/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Folder ini berisi pondasi untuk domain events: definisi event, publisher, dispatcher, dan handler.
|
||||||
|
|
||||||
|
Isi yang direkomendasikan:
|
||||||
|
- event.interface.ts (DomainEvent)
|
||||||
|
- event-publisher.ts (interface dan implementasi sederhana)
|
||||||
|
- event-dispatcher.ts (dispatcher untuk publish/dispatch event)
|
||||||
|
- event-handler.ts (type alias untuk handler)
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Event hanya bermakna untuk komunikasi lintas module atau untuk memisahkan side-effect.
|
||||||
|
- Hindari business logic kompleks di handler; handler sebaiknya mendelegasikan ke application service atau job queue.
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { DomainEvent } from './event.interface';
|
||||||
|
|
||||||
|
export class EventDispatcher {
|
||||||
|
private publisher = new (require('./event-publisher').InMemoryEventPublisher)();
|
||||||
|
|
||||||
|
register(handler: (e: DomainEvent) => Promise<void>) {
|
||||||
|
this.publisher.register(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
async dispatch(event: DomainEvent) {
|
||||||
|
await this.publisher.publish(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { DomainEvent } from './event.interface';
|
||||||
|
|
||||||
|
export type EventHandler = (event: DomainEvent) => Promise<void>;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { DomainEvent } from './event.interface';
|
||||||
|
|
||||||
|
export interface EventPublisher {
|
||||||
|
publish(event: DomainEvent): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InMemoryEventPublisher implements EventPublisher {
|
||||||
|
private handlers: Array<(e: DomainEvent) => Promise<void>> = [];
|
||||||
|
|
||||||
|
register(handler: (e: DomainEvent) => Promise<void>) {
|
||||||
|
this.handlers.push(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
async publish(event: DomainEvent) {
|
||||||
|
await Promise.all(this.handlers.map((h) => h(event)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export interface DomainEvent {
|
||||||
|
readonly name: string;
|
||||||
|
readonly payload: any;
|
||||||
|
readonly occurredAt: Date;
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
core/exceptions/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Berisi global exception filters dan custom exception classes.
|
||||||
|
|
||||||
|
Isi yang direkomendasikan:
|
||||||
|
- http-exception.filter.ts
|
||||||
|
- domain-exception.ts (custom domain exception base)
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Exception filter mengubah exception menjadi response standar API.
|
||||||
|
- Gunakan kode error konsisten (error code constant) agar client dapat menangani error.
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { ExceptionFilter, Catch, ArgumentsHost } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Catch()
|
||||||
|
export class HttpExceptionFilter implements ExceptionFilter {
|
||||||
|
catch(exception: any, host: ArgumentsHost) {
|
||||||
|
const ctx = host.switchToHttp();
|
||||||
|
const response = ctx.getResponse();
|
||||||
|
const status = exception.getStatus ? exception.getStatus() : 500;
|
||||||
|
|
||||||
|
response.status(status).json({
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: exception.code || 'INTERNAL_ERROR',
|
||||||
|
message: exception.message || 'Internal server error',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
core/logger/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Folder ini berisi wrapper logger (mis. pino) dan utilitas logging global.
|
||||||
|
|
||||||
|
Isi yang direkomendasikan:
|
||||||
|
- logger.service.ts / logger.instance.ts (exported pino instance or injectable wrapper)
|
||||||
|
- logger.interceptor.ts (optional HTTP logging interceptor)
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Gunakan structured logging (json) dengan field requestId, module, level.
|
||||||
|
- Jangan menambahkan business logic ke logger.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import pino from 'pino';
|
||||||
|
|
||||||
|
export const logger = pino({
|
||||||
|
level: process.env.LOG_LEVEL || 'info',
|
||||||
|
});
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
core/middleware/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Berisi middleware global seperti request-id, correlation id, timing, rate-limit glue (middleware only, implementasi rate-limit ada di infra).
|
||||||
|
|
||||||
|
Isi yang direkomendasikan:
|
||||||
|
- request-id.middleware.ts
|
||||||
|
- correlation-id.middleware.ts
|
||||||
|
- timing.middleware.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Middleware harus ringan dan synchronous jika memungkinkan.
|
||||||
|
- Jangan letakkan business logic di middleware.
|
||||||
|
- Middleware global didaftarkan di main.ts atau CoreModule.
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { Injectable, NestMiddleware } from '@nestjs/common';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RequestIdMiddleware implements NestMiddleware {
|
||||||
|
use(req: any, res: any, next: () => void) {
|
||||||
|
req.requestId = req.headers['x-request-id'] || uuidv4();
|
||||||
|
res.setHeader('X-Request-ID', req.requestId);
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
Queue layer placeholder.
|
||||||
|
|
||||||
|
Structure:
|
||||||
|
- jobs/: job definitions
|
||||||
|
- workers/: worker implementations
|
||||||
|
- processors/: processors that execute jobs
|
||||||
|
|
||||||
|
Queue integration (e.g. BullMQ) should be added in infrastructure phase.
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
core/queue/jobs/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Tempat definisi job (job payload shape, job name constants). Job definitions hanya mendefinisikan contract untuk job.
|
||||||
|
|
||||||
|
Contoh:
|
||||||
|
- media-scan.job.ts
|
||||||
|
- thumbnail.job.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Job definition tidak mengeksekusi logic; worker/processors yang mengeksekusi.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
core/queue/processors/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Processor berisi fungsi yang mengeksekusi pekerjaan sebenarnya (CPU/IO heavy). Processor harus idempotent dan memiliki retry strategy.
|
||||||
|
|
||||||
|
Contoh:
|
||||||
|
- media-scan.processor.ts
|
||||||
|
- thumbnail.processor.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Processor harus bisa dijalankan terpisah (CLI/worker process).
|
||||||
|
- Jangan panggil controller dari processor; gunakan service/infrastructure.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
core/queue/workers/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Worker mengkonsumsi job dari queue dan memanggil processor yang sesuai.
|
||||||
|
|
||||||
|
Contoh:
|
||||||
|
- media-scan.worker.ts
|
||||||
|
- thumbnail.worker.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Worker hanya fokus pada retry/failure handling dan delegasi ke processor.
|
||||||
|
- Implementasi queue (BullMQ, Bee-Queue) ditaruh di infra/worker deployment.
|
||||||
+48
-1
@@ -1 +1,48 @@
|
|||||||
// placeholder main.ts
|
import { ValidationPipe } from '@nestjs/common';
|
||||||
|
import { NestFactory } from '@nestjs/core';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
|
async function bootstrap() {
|
||||||
|
const app = await NestFactory.create(AppModule);
|
||||||
|
|
||||||
|
const config = app.get(ConfigService);
|
||||||
|
|
||||||
|
app.setGlobalPrefix('api');
|
||||||
|
|
||||||
|
app.useGlobalPipes(
|
||||||
|
new ValidationPipe({
|
||||||
|
whitelist: true,
|
||||||
|
transform: true,
|
||||||
|
forbidNonWhitelisted: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
app.enableCors();
|
||||||
|
|
||||||
|
const swaggerEnabled =
|
||||||
|
config.get<string>('SWAGGER_ENABLED') === 'true';
|
||||||
|
|
||||||
|
if (swaggerEnabled) {
|
||||||
|
const swaggerConfig = new DocumentBuilder()
|
||||||
|
.setTitle('RayLab Core API')
|
||||||
|
.setDescription('RayLab Core REST API')
|
||||||
|
.setVersion('1.0.0')
|
||||||
|
.addBearerAuth()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
const document = SwaggerModule.createDocument(app, swaggerConfig);
|
||||||
|
|
||||||
|
SwaggerModule.setup('docs', app, document);
|
||||||
|
}
|
||||||
|
|
||||||
|
const port = config.get<number>('PORT') || 3000;
|
||||||
|
|
||||||
|
await app.listen(port);
|
||||||
|
|
||||||
|
console.log(`Server running on http://localhost:${port}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
bootstrap();
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
modules/
|
||||||
|
|
||||||
|
Penjelasan singkat:
|
||||||
|
Folder modules berisi domain modules. Setiap module merepresentasikan satu domain bisnis dan mengikuti prinsip Clean Architecture dengan folder:
|
||||||
|
- presentation/ # controllers, dto, response
|
||||||
|
- application/ # use-cases, commands, queries
|
||||||
|
- domain/ # entities, value objects, repository interfaces, domain services, events
|
||||||
|
- infrastructure/ # prisma repositories, adapter implementations
|
||||||
|
|
||||||
|
Contoh module: identity
|
||||||
|
- src/modules/identity/presentation/controllers/users.controller.ts
|
||||||
|
- src/modules/identity/application/services/get-user.service.ts
|
||||||
|
- src/modules/identity/domain/entities/user.entity.ts
|
||||||
|
- src/modules/identity/infrastructure/repositories/user.repository.ts
|
||||||
|
|
||||||
|
Panduan singkat:
|
||||||
|
- Controller tipis: lakukan validasi dan panggil application service.
|
||||||
|
- Business logic harus berada di application/domain.
|
||||||
|
- Infrastruktur mengimplementasikan interface domain (dependency inversion).
|
||||||
|
- Gunakan event untuk komunikasi lintas module bila memungkinkan.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
|
||||||
|
export const Permissions = (...permissions: string[]) =>
|
||||||
|
SetMetadata('permissions', permissions);
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import {
|
||||||
|
Injectable,
|
||||||
|
CanActivate,
|
||||||
|
ExecutionContext,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { Reflector } from '@nestjs/core';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PermissionGuard implements CanActivate {
|
||||||
|
constructor(
|
||||||
|
private readonly reflector: Reflector,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
canActivate(context: ExecutionContext): boolean {
|
||||||
|
const permissions =
|
||||||
|
this.reflector.getAllAndOverride<string[]>(
|
||||||
|
'permissions',
|
||||||
|
[
|
||||||
|
context.getHandler(),
|
||||||
|
context.getClass(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!permissions) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const request = context.switchToHttp().getRequest();
|
||||||
|
|
||||||
|
const user = request.user;
|
||||||
|
|
||||||
|
return permissions.every(permission =>
|
||||||
|
user.permissions.includes(permission),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
modules/identity/application/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Layer application berisi use-case (services/commands/queries) yang mengorkestrasi domain dan infrastruktur.
|
||||||
|
|
||||||
|
Contoh file:
|
||||||
|
- services/get-user.service.ts
|
||||||
|
- commands/create-user.command.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Application service boleh memanggil repository interface, domain services, dan event publisher.
|
||||||
|
- Application menangani transaksi jika diperlukan.
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import {
|
||||||
|
Injectable,
|
||||||
|
ConflictException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
|
||||||
|
import { CreateUserDto } from '../../presentation/dto/create-user.dto';
|
||||||
|
import { User } from '../../domain/entities/user.entity';
|
||||||
|
import { UserRepository } from '../../domain/repositories/user.repository.interface';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CreateUserHandler {
|
||||||
|
constructor(
|
||||||
|
private readonly userRepository: UserRepository,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async execute(dto: CreateUserDto): Promise<User> {
|
||||||
|
const exists = await this.userRepository.existsByEmail(dto.email);
|
||||||
|
|
||||||
|
if (exists) {
|
||||||
|
throw new ConflictException('Email already exists.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = User.create({
|
||||||
|
name: dto.name,
|
||||||
|
email: dto.email,
|
||||||
|
password: dto.password,
|
||||||
|
metadata: dto.metadata,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.userRepository.create(user);
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// this.eventDispatcher.publish(new UserCreatedEvent(user));
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import {
|
||||||
|
Injectable,
|
||||||
|
NotFoundException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
|
||||||
|
import { UserRepository } from '../../domain/repositories/user.repository.interface';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DeleteUserHandler {
|
||||||
|
constructor(
|
||||||
|
private readonly userRepository: UserRepository,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async execute(id: string): Promise<void> {
|
||||||
|
const user = await this.userRepository.findById(id);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new NotFoundException('User not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
user.delete();
|
||||||
|
|
||||||
|
await this.userRepository.update(user);
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// Publish UserDeletedEvent
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import {
|
||||||
|
Injectable,
|
||||||
|
NotFoundException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
|
||||||
|
import { UserRepository } from '../../domain/repositories/user.repository.interface';
|
||||||
|
import { User } from '../../domain/entities/user.entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class FindUserHandler {
|
||||||
|
constructor(
|
||||||
|
private readonly userRepository: UserRepository,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async execute(id: string): Promise<User> {
|
||||||
|
const user = await this.userRepository.findById(id);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new NotFoundException('User not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { UserRepository } from '../../domain/repositories/user.repository.interface';
|
||||||
|
import { User } from '../../domain/entities/user.entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class FindUsersHandler {
|
||||||
|
constructor(
|
||||||
|
private readonly userRepository: UserRepository,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async execute(): Promise<User[]> {
|
||||||
|
return await this.userRepository.findAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import {
|
||||||
|
Injectable,
|
||||||
|
ConflictException,
|
||||||
|
NotFoundException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
|
||||||
|
import { UpdateUserDto } from '../../presentation/dto/update-user.dto';
|
||||||
|
import { UserRepository } from '../../domain/repositories/user.repository.interface';
|
||||||
|
import { User } from '../../domain/entities/user.entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UpdateUserHandler {
|
||||||
|
constructor(
|
||||||
|
private readonly userRepository: UserRepository,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
id: string,
|
||||||
|
dto: UpdateUserDto,
|
||||||
|
): Promise<User> {
|
||||||
|
const user = await this.userRepository.findById(id);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new NotFoundException('User not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
dto.email &&
|
||||||
|
dto.email !== user.email
|
||||||
|
) {
|
||||||
|
const exists =
|
||||||
|
await this.userRepository.existsByEmail(dto.email);
|
||||||
|
|
||||||
|
if (exists) {
|
||||||
|
throw new ConflictException(
|
||||||
|
'Email already exists.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
user.changeEmail(dto.email);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dto.name) {
|
||||||
|
user.changeName(dto.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.userRepository.update(user);
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
services/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Application services (use-cases) untuk module identity.
|
||||||
|
|
||||||
|
Contoh file:
|
||||||
|
- get-user.service.ts
|
||||||
|
- create-user.service.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Application service mengorkestrasi domain services dan repository.
|
||||||
|
- Menangani transaction boundary jika perlu.
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import { Controller, Get, Param } from '@nestjs/common';
|
|
||||||
import { ApiTags, ApiOperation } from '@nestjs/swagger';
|
|
||||||
import { UserService } from '../services/user.service';
|
|
||||||
|
|
||||||
@ApiTags('users')
|
|
||||||
@Controller('api/v1/users')
|
|
||||||
export class UsersController {
|
|
||||||
constructor(private readonly userService: UserService) {}
|
|
||||||
|
|
||||||
@Get(':id')
|
|
||||||
@ApiOperation({ summary: 'Get user by id' })
|
|
||||||
async findById(@Param('id') id: string) {
|
|
||||||
const data = await this.userService.findById(id);
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
data,
|
|
||||||
meta: {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
modules/identity/domain/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Layer domain berisi entity, value objects, repository interfaces, domain services, dan domain events.
|
||||||
|
|
||||||
|
Contoh file:
|
||||||
|
- entities/user.entity.ts
|
||||||
|
- events/user-created.event.ts
|
||||||
|
- repositories/user.repository.interface.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Semua aturan bisnis inti berada di domain.
|
||||||
|
- Infrastruktur mengimplementasikan interface yang didefinisikan di domain.
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
entities/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Entity domain untuk identity, mis. User, Profile.
|
||||||
|
|
||||||
|
Contoh file:
|
||||||
|
- user.entity.ts
|
||||||
|
- profile.entity.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Entity berisi atribut dan mungkin method domain kecil (invariants), bukan orchestration.
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
export class User {
|
||||||
|
private constructor(
|
||||||
|
public readonly id: string,
|
||||||
|
public name: string,
|
||||||
|
public email: string,
|
||||||
|
public password: string,
|
||||||
|
public metadata?: Record<string, any>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
static create(data: {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
metadata?: Record<string, any>;
|
||||||
|
}): User {
|
||||||
|
return new User(
|
||||||
|
crypto.randomUUID(),
|
||||||
|
data.name,
|
||||||
|
data.email,
|
||||||
|
data.password,
|
||||||
|
data.metadata,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete() {
|
||||||
|
// Business Rule
|
||||||
|
}
|
||||||
|
|
||||||
|
changeEmail(email: string) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
changeName(name: string) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static restore(data: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
metadata?: Record<string, any>;
|
||||||
|
}): User {
|
||||||
|
return new User(
|
||||||
|
data.id,
|
||||||
|
data.name,
|
||||||
|
data.email,
|
||||||
|
data.password,
|
||||||
|
data.metadata,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { DomainEvent } from '../../../../core/events/event.interface';
|
||||||
|
|
||||||
|
export class UserCreatedEvent implements DomainEvent {
|
||||||
|
readonly name = 'UserCreated';
|
||||||
|
constructor(public readonly payload: any, public readonly occurredAt: Date = new Date()) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { DomainEvent } from '../../../../core/events/event.interface';
|
||||||
|
|
||||||
|
export class UserDeletedEvent implements DomainEvent {
|
||||||
|
readonly name = 'UserDeleted';
|
||||||
|
constructor(public readonly payload: any, public readonly occurredAt: Date = new Date()) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { User } from '../entities/user.entity';
|
||||||
|
|
||||||
|
export abstract class UserRepository {
|
||||||
|
abstract create(user: User): Promise<User>;
|
||||||
|
|
||||||
|
abstract update(user: User): Promise<User>;
|
||||||
|
|
||||||
|
abstract findById(
|
||||||
|
id: string,
|
||||||
|
): Promise<User | null>;
|
||||||
|
|
||||||
|
abstract findAll(): Promise<User[]>;
|
||||||
|
|
||||||
|
abstract existsByEmail(
|
||||||
|
email: string,
|
||||||
|
): Promise<boolean>;
|
||||||
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
// REMOVED: file content deleted per user request
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
// REMOVED: file content deleted per user request
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
export class UserEntity {
|
|
||||||
id: string;
|
|
||||||
email: string;
|
|
||||||
displayName: string;
|
|
||||||
metadata?: Record<string, any>;
|
|
||||||
createdAt: Date;
|
|
||||||
updatedAt: Date;
|
|
||||||
deletedAt?: Date | null;
|
|
||||||
}
|
|
||||||
@@ -1 +1,52 @@
|
|||||||
// REMOVED: file content deleted per user request
|
import { Module } from '@nestjs/common';
|
||||||
|
import { UsersController } from './presentation/controllers/users.controller';
|
||||||
|
import { PrismaService } from '../../shared/prisma.service';
|
||||||
|
import { PrismaUserRepository } from './infrastructure/repositories/prisma-user.repository';
|
||||||
|
import { UserRepository } from './domain/repositories/user.repository.interface';
|
||||||
|
import { EventDispatcher } from '../../core/events/event-dispatcher';
|
||||||
|
import { JwtAuthGuard } from '../../core/auth/guards/jwt-auth.guard';
|
||||||
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
|
import { CreateUserHandler } from './application/handlers/create-user.handler';
|
||||||
|
import { FindUserHandler } from './application/handlers/find-user.handler';
|
||||||
|
import { DeleteUserHandler } from './application/handlers/delete-user.handler';
|
||||||
|
import { FindUsersHandler } from './application/handlers/find-users.handler';
|
||||||
|
import { UpdateUserHandler } from './application/handlers/update-user.handler';
|
||||||
|
import { PermissionGuard } from '../authorization/presentation/guards/permission.guard';
|
||||||
|
import { Reflector } from '@nestjs/core';
|
||||||
|
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
JwtModule.register({
|
||||||
|
secret: process.env.JWT_SECRET,
|
||||||
|
signOptions: {
|
||||||
|
expiresIn: '1d',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
controllers: [UsersController],
|
||||||
|
providers: [
|
||||||
|
//#region User
|
||||||
|
CreateUserHandler,
|
||||||
|
FindUserHandler,
|
||||||
|
FindUsersHandler,
|
||||||
|
UpdateUserHandler,
|
||||||
|
DeleteUserHandler,
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
JwtAuthGuard,
|
||||||
|
PermissionGuard,
|
||||||
|
Reflector,
|
||||||
|
PrismaService,
|
||||||
|
PrismaUserRepository,
|
||||||
|
{
|
||||||
|
provide: UserRepository,
|
||||||
|
useClass: PrismaUserRepository,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: 'EVENT_DISPATCHER',
|
||||||
|
useValue: new EventDispatcher(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class IdentityModule {}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
modules/identity/infrastructure/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Implementasi teknis untuk module identity, seperti Prisma repository, adapter implementations, dan data mappers.
|
||||||
|
|
||||||
|
Contoh file:
|
||||||
|
- prisma/user.repository.ts (mengimplementasikan domain repository interface)
|
||||||
|
- adapter/identity-adapter.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Infrastruktur hanya mengimplementasikan interface domain; jangan memuat business rules.
|
||||||
|
- Import dari infrastructure ke domain harus satu arah: infrastructure -> domain (implementasi).
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { User } from '../../domain/entities/user.entity';
|
||||||
|
|
||||||
|
export class PrismaUserMapper {
|
||||||
|
static toDomain(model: any): User | null {
|
||||||
|
if (!model) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return User.restore({
|
||||||
|
id: model.id,
|
||||||
|
name: model.name,
|
||||||
|
email: model.email,
|
||||||
|
password: model.password,
|
||||||
|
metadata: model.metadata,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static toPersistence(user: User) {
|
||||||
|
return {
|
||||||
|
id: user.id,
|
||||||
|
name: user.name,
|
||||||
|
email: user.email,
|
||||||
|
password: user.password,
|
||||||
|
metadata: user.metadata ?? {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
repositories/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Implementasi repository di layer infrastructure. Biasanya berisi Prisma queries dan mapping antara DB model dan domain entity.
|
||||||
|
|
||||||
|
Contoh file:
|
||||||
|
- prisma/user.repository.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Repository mengimplementasikan interface di domain layer.
|
||||||
|
- Hindari business logic di repository.
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { PrismaService } from '../../../../shared/prisma.service';
|
||||||
|
import { UserRepository } from '../../domain/repositories/user.repository.interface';
|
||||||
|
import { User } from '../../domain/entities/user.entity';
|
||||||
|
import { PrismaUserMapper } from '../mappers/prisma-user.mapper';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PrismaUserRepository implements UserRepository {
|
||||||
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
|
|
||||||
|
async findById(id: string) {
|
||||||
|
const user = await this.prisma.user.findUnique({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
return PrismaUserMapper.toDomain(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAll(): Promise<User[]> {
|
||||||
|
const users = await this.prisma.user.findMany({
|
||||||
|
where: { deleted_at: null },
|
||||||
|
orderBy: { created_at: 'desc' },
|
||||||
|
});
|
||||||
|
|
||||||
|
return users.map(PrismaUserMapper.toDomain);
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(user: User): Promise<User> {
|
||||||
|
const created = await this.prisma.user.create({
|
||||||
|
data: PrismaUserMapper.toPersistence(user),
|
||||||
|
});
|
||||||
|
|
||||||
|
return PrismaUserMapper.toDomain(created)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(user: User): Promise<User> {
|
||||||
|
const updated = await this.prisma.user.update({
|
||||||
|
where: {
|
||||||
|
id: user.id,
|
||||||
|
},
|
||||||
|
data: PrismaUserMapper.toPersistence(user),
|
||||||
|
});
|
||||||
|
|
||||||
|
return PrismaUserMapper.toDomain(updated)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(id: string) {
|
||||||
|
const user = await this.prisma.user.update({
|
||||||
|
where: { id },
|
||||||
|
data: { deleted_at: new Date() },
|
||||||
|
});
|
||||||
|
return PrismaUserMapper.toDomain(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
async existsByEmail(email: string, excludeId?: string | null) {
|
||||||
|
const where: any = { email };
|
||||||
|
if (excludeId) {
|
||||||
|
where.id = { not: excludeId };
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await this.prisma.user.findFirst({ where });
|
||||||
|
return !!user;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export interface IUserService {
|
|
||||||
findById(id: string): Promise<any | null>;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
modules/identity/presentation/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Layer presentation untuk module identity. Berisi controller, request/response DTO, dan mapping ke HTTP.
|
||||||
|
|
||||||
|
Contoh file:
|
||||||
|
- controllers/users.controller.ts
|
||||||
|
- dto/create-user.dto.ts
|
||||||
|
- dto/update-user.dto.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Controller tipis: lakukan validasi DTO dan panggil application service.
|
||||||
|
- Jangan menaruh business logic di controller.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
controllers/
|
||||||
|
|
||||||
|
Penjelasan:
|
||||||
|
Folder untuk controller HTTP endpoint module identity.
|
||||||
|
|
||||||
|
Contoh file:
|
||||||
|
- users.controller.ts
|
||||||
|
|
||||||
|
Aturan:
|
||||||
|
- Controller hanya menerima request, validasi DTO, panggil application service, kembalikan response.
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user