Files
satusehat-worker/docs/QMD.md
T
2026-07-29 06:38:42 +00:00

219 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🛡️ 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.*