# 🛡️ 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//` 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.` 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 ` 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//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 ` atau `-j `. - 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.*