# 🛡️ 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.*
