update ignore
This commit is contained in:
No files matched your search
+218
@@ -0,0 +1,218 @@
|
||||
# 🛡️ Quality Management Document (QMD)
|
||||
|
||||
**Project:** GoPrint Service General (`worker-satusehat`)
|
||||
**Versi:** 1.0
|
||||
**Tanggal:** 2026-05-13
|
||||
|
||||
Dokumen ini merangkum **standar mutu, pola tata-kelola kode, dan kontrol resiko** yang berlaku pada codebase. Setiap item disertai titik verifikasi (file/fungsi) supaya bisa diaudit ulang.
|
||||
|
||||
---
|
||||
|
||||
## 1. Standar Arsitektur
|
||||
|
||||
### 1.1 Clean Architecture + CQRS
|
||||
|
||||
Setiap modul bisnis (`auth`, `master/role/*`, `master/kfa`, `worker/satusehat/*`) **WAJIB** terdiri minimal dari:
|
||||
|
||||
- `entity.go` — definisi domain (GORM struct).
|
||||
- `dto.go` — request/response & filter.
|
||||
- `repository.go` — terpisah `CommandRepository` (GORM, primary) dan `QueryRepository` (sqlx, read replica).
|
||||
- `service.go` — orkestrasi, validasi, integrasi cache.
|
||||
- `mapper.go` — DTO ↔ Entity (anti-leak GORM tag).
|
||||
|
||||
> Generator boilerplate tersedia: lihat [scripts/](scripts/) dan `tools/generate.go`. Wajib digunakan untuk modul baru supaya struktur konsisten.
|
||||
|
||||
### 1.2 Pembagian Tanggung Jawab Worker
|
||||
|
||||
Setiap worker SatuSehat di `internal/worker/satusehat/<resource>/` menggunakan kontrak yang seragam:
|
||||
|
||||
- `Config{ DBManager, InternalBaseURL, OrganizationID }`
|
||||
- `TokenManager` interface (`GetAccessToken`, `ForceRefreshAndGetToken`) — di-implement oleh `worker.Manager`.
|
||||
- `Run(ctx context.Context)` — long-running loop dengan select-context untuk *graceful shutdown*.
|
||||
- Tracker per modul (`last_*_id.txt` / `last_*_time.txt`) — *to be migrated to DB*.
|
||||
|
||||
---
|
||||
|
||||
## 2. Observability & Logging
|
||||
|
||||
### 2.1 Logger (`pkg/logger`)
|
||||
|
||||
- Wrapper **Logrus** dengan format JSON otomatis di mode `production`, text-formatted di `development`.
|
||||
- **`localTimeHook`** memaksa timestamp = `Asia/Jakarta` (WIB). Tidak boleh ada log dengan timezone lain.
|
||||
- Rotasi harian: `logs/YYYY/MM/YYYY-MM-DD.log` (writer ditutup & dibuka ulang per hari).
|
||||
- Helper field: `logger.String/Int/Int64/Bool/Any/ErrorField` untuk konsistensi key.
|
||||
- `caller` & `function_name` aktif otomatis ketika `Environment != "production"`.
|
||||
|
||||
### 2.2 Metrics
|
||||
|
||||
- `prometheus/client_golang` ter-import. Belum ada handler `/metrics` yang terpasang di routes — **TODO** sebelum production multi-instance.
|
||||
|
||||
### 2.3 Tracing
|
||||
|
||||
- Field `request_id` (UUID) wajib di-inject melalui middleware HTTP. Untuk worker, gunakan ID dokumen yang sedang diproses (`idxpesanobat`, `kfa_code`) sebagai correlation ID.
|
||||
|
||||
---
|
||||
|
||||
## 3. Database Resilience
|
||||
|
||||
### 3.1 Connection Pool
|
||||
|
||||
Setiap entry `databases.<name>` di `config.yaml` wajib mendefinisikan:
|
||||
|
||||
```yaml
|
||||
max_open_conns: 25
|
||||
max_idle_conns: 25
|
||||
conn_max_lifetime: 5m
|
||||
```
|
||||
|
||||
Jika `max_open_conns` tidak diatur, infrastructure menerapkan formula default `(CPU_Cores * 2) + 1`.
|
||||
|
||||
### 3.2 Multi-DB & Read Replica
|
||||
|
||||
- Database Manager mendukung **PostgreSQL, MySQL, SQL Server, SQLite, MongoDB** dalam satu service.
|
||||
- `CommandRepository` selalu menulis ke primary; `QueryRepository` membaca dari replica (round-robin) jika dikonfigurasi.
|
||||
- **SLOW QUERY THRESHOLD: 200 ms** (GORM logger adapter); query di atas ambang ini dilog warn level dengan SQL + duration.
|
||||
|
||||
### 3.3 Query Builder (`pkg/utils/query`)
|
||||
|
||||
- Whitelist kolom + dialect-aware (Postgres, MySQL, MSSQL).
|
||||
- Pencegahan SQL injection wajib via parameter binding — *no string concat di repository*.
|
||||
- Tests: [sql_builder_test.go](pkg/utils/query/sql_builder_test.go), [dialects_test.go](pkg/utils/query/dialects_test.go).
|
||||
|
||||
---
|
||||
|
||||
## 4. Keamanan
|
||||
|
||||
### 4.1 Secret Management
|
||||
|
||||
- Semua kredensial dilarang hardcoded. Akses via env var atau Viper dari `.env` / `.env.prod`.
|
||||
- **Anomali yang diketahui** ([patient/migrator.go:23-26](internal/worker/patient/migrator.go#L23-L26)): `defaultUsername "adm_pendaftaran"` & `defaultPassword "1234"`. **WAJIB dihapus** atau dijadikan env-only sebelum file ini diaktifkan.
|
||||
|
||||
### 4.2 Auth
|
||||
|
||||
- Provider pluggable: `jwt`, `keycloak`, `static`, `hybrid` (config `auth.type` + `auth.fallback_to`).
|
||||
- JWT signing key di env; Keycloak JWKS URL di env.
|
||||
- Rate limit per IP dikelola Redis (`security.rate_limit.requests_per_minute`, default 60).
|
||||
|
||||
### 4.3 Crypto BPJS
|
||||
|
||||
- HMAC-SHA256 untuk signature (`X-cons-id` + `&` + `X-timestamp`).
|
||||
- Dekripsi AES-256-CBC (`bpjs/crypto.go`):
|
||||
- key = `SHA256(ConsID + SecretKey + Timestamp)`
|
||||
- IV = 16 byte pertama dari key
|
||||
- Padding PKCS7
|
||||
- Auto-decompression `LZString → Gzip → plaintext` ([decompression.go](internal/interfaces/bpjs/decompression.go)).
|
||||
|
||||
### 4.4 SatuSehat OAuth2
|
||||
|
||||
- Client-Credentials flow; token cached in-memory dengan **safety margin 60 detik** sebelum exp.
|
||||
- Pada 401, request retry otomatis setelah `ForceRefreshAndGetToken` ([satusehat/client.go](internal/interfaces/satusehat/client.go)).
|
||||
- Mutex `tokenMutex` di [worker.Manager](internal/worker/worker.go#L25-L27) mencegah race antar worker.
|
||||
|
||||
### 4.5 RBAC Caching
|
||||
|
||||
- Kunci cache permission menggunakan SHA-256 dari (role_id, page_id, parameter query) — bukan plaintext.
|
||||
- *Invalidate by prefix* otomatis pada `Create/Update/Delete`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Resiliensi Worker
|
||||
|
||||
### 5.1 Initial Login Retry
|
||||
|
||||
- 6× percobaan dengan jeda 5 detik. Jika semua gagal → `Fatal` (service mati). Lihat [worker.go:42-55](internal/worker/worker.go#L42-L55).
|
||||
|
||||
### 5.2 Rate Limit Handling (per worker)
|
||||
|
||||
| Worker | Strategi |
|
||||
| :--- | :--- |
|
||||
| KFA Master Puller | 30s list API, 60s detail API, 500ms antar item, idle 6h saat halaman kosong |
|
||||
| Medication / MedicationRequest / MedicationDispense | `rateLimitSleep = 60s` + retry dokumen yang sama (tidak skip ID) |
|
||||
| ImagingStudy / ServiceRequest | Pola serupa, time-based tracker |
|
||||
|
||||
> Deteksi rate-limit fleksibel: cek status code 429 *atau* substring respons (`"too many requests"`, `"rate limit"`, `"limit exceeded"`, `"limit pengiriman"`, `"quota exceeded"`).
|
||||
|
||||
### 5.3 Token Recovery
|
||||
|
||||
- Tiap worker yang memanggil API internal `service-satusehat` *wajib*:
|
||||
1. Sertakan `Authorization: Bearer <token>` dari `TokenManager.GetAccessToken()`.
|
||||
2. Jika respons 401 → `ForceRefreshAndGetToken` lalu retry sekali.
|
||||
3. Jika refresh gagal → fallback re-login ([auth.go:139-149](internal/worker/auth.go#L139-L149)).
|
||||
|
||||
### 5.4 Graceful Shutdown
|
||||
|
||||
- Root `context` dengan `errgroup` (`golang.org/x/sync/errgroup`).
|
||||
- Sinyal `SIGINT`/`SIGTERM` → `cancel()` → semua goroutine select-context exit → `g.Wait()`.
|
||||
- Tidak boleh ada `for { ... }` tanpa `select { case <-ctx.Done(): return; default: ... }` di kode worker.
|
||||
|
||||
### 5.5 Staggered Startup
|
||||
|
||||
- `baseDelay = 10s` antar tiap worker untuk mencegah *thundering herd* terhadap API gov't ([worker.go:268-303](internal/worker/worker.go#L268-L303)).
|
||||
|
||||
---
|
||||
|
||||
## 6. State Management Worker
|
||||
|
||||
### 6.1 Tracker File (Praktik Saat Ini)
|
||||
|
||||
- Lokasi: `internal/worker/satusehat/<resource>/last_*.txt` & `internal/master/kfa/kfa_tracker.txt`.
|
||||
- Format: integer ID (medication, KFA, imagingstudy) atau ISO timestamp (servicerequest radiology).
|
||||
- Mapping FHIR ID disimpan harian: `mapperdata/YYYY-MM/YYYY-MM-DD.txt` berformat `idxpesanobat|fhir_id`.
|
||||
|
||||
> **Batasan kritikal:** tidak aman untuk multi-replica. **Wajib** dipindah ke DB / Redis untuk deployment Kubernetes (lihat Devplan Phase 2).
|
||||
|
||||
### 6.2 Sync Log
|
||||
|
||||
- Setiap pengiriman → record `SyncLog` (request payload, response, status `SUCCESS`/`FAILED`/`RATE_LIMITED`, error message) tersimpan di tabel SIMRS untuk audit.
|
||||
|
||||
---
|
||||
|
||||
## 7. Standar Penulisan Kode
|
||||
|
||||
### 7.1 Linting & Format
|
||||
|
||||
- `go fmt` mandatory (CI hook disarankan).
|
||||
- Komentar fungsi publik dalam Bahasa Indonesia diperbolehkan (codebase saat ini multilingual); konsistensi per file diutamakan.
|
||||
|
||||
### 7.2 Error Handling
|
||||
|
||||
- Gunakan `pkg/errors.AppError` untuk error berkode (HTTP/gRPC mapping otomatis).
|
||||
- *Wrapping*: `fmt.Errorf("konteks: %w", err)` — selalu `%w` jika ingin di-unwrap.
|
||||
- Worker **tidak boleh panic**; pakai `logger.Error` + retry/skip.
|
||||
|
||||
### 7.3 Testing
|
||||
|
||||
- File `*_test.go` wajib di package `pkg/utils/query`. Coverage modul bisnis saat ini rendah — **TODO** unit test layer service.
|
||||
|
||||
### 7.4 Generator Boilerplate
|
||||
|
||||
- Modul baru dibuat via `scripts/context.sh -s <file.sql>` atau `-j <file.json>`.
|
||||
- Generator menghasilkan: entity, DTO, repository (Command + Query), service, mapper, gRPC proto draft.
|
||||
|
||||
---
|
||||
|
||||
## 8. Dependensi & Lisensi
|
||||
|
||||
- Go runtime: **1.25** (go.mod baris 3).
|
||||
- Web: Gin 1.10, gRPC 1.78, Swagger via `swaggo/swag` 1.16.
|
||||
- Persistence: GORM 1.30 (postgres/mysql/sqlserver/sqlite driver), sqlx 1.4, MongoDB driver 1.17.
|
||||
- Cache & infra: redis/go-redis v9.17, MinIO Go v7.0.98, Prometheus client 1.23.
|
||||
- Crypto: golang.org/x/crypto v0.49, JWT golang-jwt v5.2.
|
||||
- Util: logrus 1.9, viper-style config via `karincake/apem`.
|
||||
- License: Apache 2.0 (lihat header swagger).
|
||||
|
||||
---
|
||||
|
||||
## 9. Checklist Audit Periodik
|
||||
|
||||
- [ ] Semua tracker `.txt` masih dibutuhkan & sinkron dengan DB.
|
||||
- [ ] Tidak ada secret di repository (`grep -r "password.*=.*\"" .` clean).
|
||||
- [ ] `logger.Default()` digunakan; tidak ada `fmt.Println` di production path.
|
||||
- [ ] Setiap worker punya rate-limit handler + token retry.
|
||||
- [ ] Cache TTL & invalidation pattern terdokumentasi per modul.
|
||||
- [ ] Migration script idempotent.
|
||||
- [ ] Prometheus `/metrics` endpoint live & terkonsumsi monitoring stack.
|
||||
|
||||
---
|
||||
|
||||
*Disusun ulang: Tim Engineering GoPrint, 2026-05-13.*
|
||||
Reference in New Issue
Block a user