251 lines
9.2 KiB
Markdown
251 lines
9.2 KiB
Markdown
# 🗺️ Development Plan (Roadmap)
|
||
|
||
**Project:** GoPrint Service General (`worker-satusehat`)
|
||
**Versi:** 1.0
|
||
**Tanggal:** 2026-05-13
|
||
|
||
Peta jalan terurut berdasarkan **dampak vs effort**. Setiap fase punya keluaran yang bisa dirilis berdiri sendiri.
|
||
|
||
---
|
||
|
||
## Phase 0 — Stabilisasi Production Saat Ini *(1–2 minggu)*
|
||
|
||
**Tujuan:** Service yang sekarang sudah jalan (REST + RBAC + KFA Puller) bebas bug kritikal.
|
||
|
||
### Langkah Stabilisasi
|
||
|
||
1. **Fix `satusehatClient = nil`** ([main.go:235](cmd/api/main.go#L235))
|
||
- Inisialisasi: `satusehatClient := satusehat.NewClient(cfg)` lalu pass ke `worker.NewManager`.
|
||
- *Acceptance:* `go run` tidak panic ketika `cfg.SatuSehat.Enabled = true`.
|
||
|
||
2. **Pindahkan kredensial worker login ke env**
|
||
- `WORKER_AUTH_EMAIL`, `WORKER_AUTH_PASSWORD` di `.env` + validation di `config.Validate()`.
|
||
- Hapus default hardcoded di [worker/auth.go:25-28](internal/worker/auth.go#L25-L28).
|
||
|
||
3. **Audit & bersihkan kode mati**
|
||
- Hapus `patient/migrator.go` jika tidak akan dipakai, atau pindahkan ke folder `archive/` + buang default password.
|
||
- Hilangkan `truncate()` duplikat: pindah ke `pkg/logger` atau `pkg/utils`.
|
||
|
||
4. **Endpoint `/metrics` Prometheus aktif**
|
||
- Tambahkan handler `promhttp.Handler()` di `routes.go`.
|
||
- Definisikan counter dasar: `worker_documents_processed_total`, `worker_rate_limit_hits_total`, `satusehat_token_refresh_total`.
|
||
|
||
5. **gRPC handler `PermissionHandler` di-uncomment**
|
||
- Tes via `grpcurl` ke `:9090`.
|
||
|
||
**Output:** Tag `v0.9.0-stable`.
|
||
|
||
---
|
||
|
||
## Phase 1 — Aktivasi Worker Klinis *(2–4 minggu)*
|
||
|
||
**Tujuan:** Sinkronisasi data obat & radiologi ke SatuSehat jalan otomatis 24/7.
|
||
|
||
### Langkah Aktivasi
|
||
|
||
1. **Aktifkan worker secara bertahap di [worker.go](internal/worker/worker.go)** dengan urutan dependensi:
|
||
1. `Encounter` (prasyarat referensi)
|
||
2. `Medication` (master kode obat lokal → KFA)
|
||
3. `MedicationRequest`
|
||
4. `MedicationDispense`
|
||
5. `ServiceRequest (Radiology)`
|
||
6. `ImagingStudy`
|
||
|
||
2. **Validasi end-to-end per worker:**
|
||
- Cek `sync_log` (status SUCCESS/FAILED/RATE_LIMITED).
|
||
- Pastikan FHIR ID kembali & tersimpan di mapping harian.
|
||
- Monitor log error rate ≤ 1% per jam selama 48 jam soak test.
|
||
|
||
3. **Rate-limit config dinamis** — pindahkan konstanta `rateLimitSleep` per worker ke `config.yaml`:
|
||
|
||
```yaml
|
||
satu_sehat:
|
||
worker:
|
||
medication: { rate_limit_sleep: 60s, poll_interval: 2s }
|
||
imagingstudy: { rate_limit_sleep: 60s, poll_interval: 2s }
|
||
```
|
||
|
||
4. **Sync Log Retention Policy**
|
||
- Tabel `sync_log` bisa membengkak — tambahkan TTL/archive job (retain 90 hari, archive ke S3/MinIO).
|
||
|
||
**Output:** Tag `v1.0.0` — release bridging klinis pertama.
|
||
|
||
---
|
||
|
||
## Phase 2 — Stateless Worker (Multi-Replica Ready) *(2–3 minggu)*
|
||
|
||
**Tujuan:** Mendukung deployment Kubernetes / multi-pod tanpa risiko race tracker.
|
||
|
||
### Langkah Refactor State
|
||
|
||
1. **Tabel `sync_tracker`**
|
||
|
||
```sql
|
||
CREATE TABLE sync_tracker (
|
||
module_name VARCHAR(64) PRIMARY KEY,
|
||
last_id BIGINT,
|
||
last_timestamp TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
```
|
||
|
||
- Implementasi via row-lock `SELECT ... FOR UPDATE SKIP LOCKED` (Postgres) sehingga hanya satu replica per modul.
|
||
|
||
2. **Refactor helper `readLastID()` / `writeTracker()`**
|
||
- Bikin abstraksi `TrackerStore` di `pkg/tracker/` (Interface: `Get`, `Set` per module name).
|
||
- Implementasi backend `FileTracker` (lama) & `DBTracker` (baru). Pilih via env `TRACKER_BACKEND=db|file`.
|
||
|
||
3. **Migrasi data tracker yang ada**
|
||
- Script one-shot: baca semua `*.txt` di `internal/worker/satusehat/*/last_*` & `internal/master/kfa/kfa_tracker.txt` → seed ke `sync_tracker`.
|
||
|
||
4. **Mapping FHIR ID → Redis hash atau tabel**
|
||
- `mapperdata/YYYY-MM/YYYY-MM-DD.txt` tidak cocok multi-pod. Pindah ke tabel `fhir_id_map(local_id, fhir_id, resource_type, created_at)`.
|
||
|
||
5. **Leader election sederhana untuk worker eksklusif**
|
||
- Gunakan Redis `SET key NX EX 60` sebagai *lease* untuk worker singleton (KFA Puller, dst).
|
||
|
||
**Output:** Tag `v1.1.0` — deployable di Kubernetes dengan ≥ 2 replica.
|
||
|
||
---
|
||
|
||
## Phase 3 — Event-Driven Architecture *(4–6 minggu)*
|
||
|
||
**Tujuan:** Mengurangi polling DB → reactive berdasarkan trigger SIMRS.
|
||
|
||
### Langkah Event-Driven
|
||
|
||
1. **PostgreSQL LISTEN/NOTIFY**
|
||
- Trigger pada tabel sumber (mis. `t_pesan_obat` AFTER INSERT) → `NOTIFY pesan_obat_new, NEW.id::text`.
|
||
- Worker `Medication` subscribe via `pq.NewListener` daripada polling per 2 detik.
|
||
- Fallback: tetap ada polling tiap N detik sebagai *catch-up* untuk event yang tertinggal.
|
||
|
||
2. **Kafka Producer (opsional, untuk multi-microservice)**
|
||
- Uncomment [main.go:111-113](cmd/api/main.go#L111-L113).
|
||
- Publish event `fhir.resource.synced` setelah pengiriman sukses untuk konsumsi service lain (audit, BI, dashboard).
|
||
- Library: `segmentio/kafka-go` (lightweight).
|
||
|
||
3. **Dead Letter Queue**
|
||
- Pesan yang gagal kirim 3× → masuk topik `fhir.dlq` untuk inspeksi manual.
|
||
|
||
4. **Webhook callback dari `service-satusehat`**
|
||
- Jika service downstream support webhook untuk async FHIR commit, langganan untuk update `sync_log.status` lebih cepat.
|
||
|
||
**Output:** Tag `v1.2.0`.
|
||
|
||
---
|
||
|
||
## Phase 4 — Worker Coverage Lengkap *(rolling, 1 resource / sprint)*
|
||
|
||
**Tujuan:** Mengisi 13 worker stub menjadi production-ready.
|
||
|
||
### Urutan Prioritas Worker (berdasarkan kebutuhan pelaporan SatuSehat)
|
||
|
||
1. `Condition` (diagnosa)
|
||
2. `Observation` (vital sign, lab)
|
||
3. `Procedure` (tindakan medis)
|
||
4. `DiagnosticReport` (laporan lab/radiologi)
|
||
5. `AllergyIntolerance`
|
||
6. `Immunization`
|
||
7. `Specimen` (lab)
|
||
8. `ClinicalImpression`
|
||
9. `EpisodeOfCare`
|
||
10. `CarePlan`
|
||
11. `Composition`
|
||
12. `MedicationStatement`
|
||
13. `QuestionnaireResponse`
|
||
|
||
### Template per Worker
|
||
|
||
- Salin pola dari worker yang sudah lengkap (saran: `Medication` untuk transactional, `ServiceRequest` untuk time-based).
|
||
- Komponen wajib: tracker, rate-limit handler, KFA/master fetch jika perlu, token refresh, sync log, mapping persistence.
|
||
|
||
**Output:** Tiap worker = sprint sendiri, release patch (`v1.x.y`).
|
||
|
||
---
|
||
|
||
## Phase 5 — Observability & Operasional *(2–3 minggu, paralel)*
|
||
|
||
### Langkah Observability
|
||
|
||
1. **Grafana Dashboard**
|
||
- Source: Prometheus `worker_*_total`, `worker_processing_duration_seconds`, `satusehat_response_status_total{code=...}`.
|
||
- Panel: throughput per worker, error rate, p95 latency, queue depth (lag dari tracker terhadap MAX(id) source).
|
||
|
||
2. **Alerting**
|
||
- Worker tidak progress > 10 menit.
|
||
- Rate-limit hits > 10/min selama 5 menit.
|
||
- Token refresh failed > 3 berturut-turut.
|
||
|
||
3. **Distributed Tracing (opsional)**
|
||
- Integrasi OpenTelemetry, trace span dari REST handler → service → repository → API gov't.
|
||
|
||
4. **Health & Readiness Probe**
|
||
- `/health/live`: cek service hidup.
|
||
- `/health/ready`: cek DB ping, Redis ping, MinIO ping, last token refresh sukses.
|
||
|
||
**Output:** Stack monitoring siap pakai (Prometheus + Grafana + Alertmanager).
|
||
|
||
---
|
||
|
||
## Phase 6 — UI Dashboard *(repository terpisah, ditangani tim frontend)*
|
||
|
||
**Tujuan:** Operasional team & admin RS bisa monitor + intervensi tanpa SSH.
|
||
|
||
### Fitur Inti Dashboard
|
||
|
||
1. **Sync Monitor**
|
||
- Tabel `sync_log` realtime, filter by status, worker, tanggal.
|
||
- Re-queue dokumen gagal (button "Retry").
|
||
|
||
2. **Tracker Cockpit**
|
||
- Posisi setiap worker (last_id, last_timestamp) vs. MAX di tabel sumber → lag visual.
|
||
- Pause / Resume worker via toggle (butuh control-plane API baru: `PUT /api/v1/worker/{name}/state`).
|
||
|
||
3. **RBAC Management**
|
||
- Hirarki halaman dengan checkbox CRUD level per role (konsumsi `GetRolePermissionTree`).
|
||
- Cache invalidation otomatis lewat REST API existing.
|
||
|
||
4. **KFA Browser**
|
||
- Search produk farmasi yang sudah masuk ke DB lokal.
|
||
- Trigger ulang puller dari halaman tertentu.
|
||
|
||
5. **BPJS Console (opsional)**
|
||
- Dropdown service VClaim/Antrol → form input → call SDK → tampilkan hasil dekripsi.
|
||
|
||
---
|
||
|
||
## Phase 7 — Hardening & Compliance *(jangka panjang)*
|
||
|
||
- **Audit Trail** lengkap untuk RBAC: siapa, kapan, mengubah apa.
|
||
- **Field-Level Encryption** untuk data PII (NIK, alamat) yang tersimpan di sync log.
|
||
- **SLA Report** untuk Kemenkes: persentase data terkirim dalam 1×24 jam.
|
||
- **Disaster Recovery** runbook: replay dari sync log, re-sync dari ID tertentu.
|
||
|
||
---
|
||
|
||
## Ringkasan Timeline Indikatif
|
||
|
||
| Phase | Durasi | Target Tag | Outcome |
|
||
| :--- | :--- | :--- | :--- |
|
||
| 0 — Stabilisasi | 1–2 minggu | `v0.9.0` | Bug kritikal fixed, metrics live |
|
||
| 1 — Aktivasi Klinis | 2–4 minggu | `v1.0.0` | Worker obat & radiologi production |
|
||
| 2 — Stateless Tracker | 2–3 minggu | `v1.1.0` | Multi-replica safe |
|
||
| 3 — Event-Driven | 4–6 minggu | `v1.2.0` | LISTEN/NOTIFY + Kafka |
|
||
| 4 — Worker Lengkap | rolling | `v1.x.y` | 13 resource FHIR aktif |
|
||
| 5 — Observability | 2–3 minggu | (paralel) | Grafana + alerting |
|
||
| 6 — UI Dashboard | terpisah | (FE repo) | Operator self-service |
|
||
| 7 — Hardening | berkelanjutan | — | Compliance & DR |
|
||
|
||
---
|
||
|
||
## Quick Wins Bulan Ini
|
||
|
||
1. **Fix BUG-001** (satusehatClient nil) — 1 jam kerja, dampak besar.
|
||
2. **Wire `/metrics`** — 30 menit, langsung kelihatan progress di Prometheus.
|
||
3. **Uncomment Worker `Medication`** + soak test 24 jam staging.
|
||
4. **Pindahkan password worker ke env** — 1 jam, tutup security finding.
|
||
|
||
---
|
||
|
||
*Disusun ulang: Tim Engineering GoPrint, 2026-05-13.*
|