1. 📌 Overview & Purpose
Goal: Buang silo per-klinik. Pesakit jumpa Klinik A hari ni, Klinik B esok — Klinik B doktor ada full context (consent-required). Allergy alert auto-propagate. Reduces duplicate tests, missed allergies, drug interactions across providers.
Network effect feature: Tak boleh berdiri sendiri — perlu ≥3 klinik dlm rangkaian untuk meaningful value. Beri klinik incentive join (network advantage > standalone).
Privacy first: Patient-controlled · per-action consent · explicit opt-in for cross-clinic share · withdraw retroactive.
2. 👤 User Stories
Saya jumpa Klinik A semalam, hari ni jumpa Klinik B sebelah · saya consent share · Klinik B doktor terus tahu sejarah saya · tak perlu repeat semua.
Saya doktor Klinik B · pesakit baru saya tak pernah jumpa · M8 alert: pesakit ada penicillin allergy registered Klinik A bulan lepas · safety net yes.
Saya nak control · share allergy +chronic condition saja, tak share encounter detail · withdraw bila-bila bila tak nak.
Saya klinik solo · join rangkaian = pesakit dpt continuity · saya dpt referral inbound · win-win.
Saya pindah ke klinik luar rangkaian · saya nak export full record (FHIR Bundle) untuk bawa.
MOH audit · saya nak lihat siapa access pesakit X, bila, kenapa · cross-tenant access trail visible.
3. ✅ Functional Requirements
4. ⚙️ Non-Functional Requirements
| Aspect | Target | Notes |
|---|---|---|
| Cross-tenant query latency | <1s p99 | Federated query · cached |
| Allergy propagation | <30s | Safety critical · broadcast |
| FHIR export latency | <30s | Async OK · email notify |
| Consent enforcement | 100% queries gated | Audit verifies |
| Audit log granularity | Per-field access | Granular trail |
| Min network size for value | ≥3 klinik · 1 hospital | Network effect threshold |
| Data residency | Malaysia only | PDPA · MOH requirement |
5. 🗄️ Data Model
| Table | Key fields | Purpose |
|---|---|---|
patient_master | id, ic_hash, mrn_global (YYYY-NNNNNN), name_canonical, dob, primary_clinic_id | Cross-tenant master |
patient_clinic_links | patient_id, clinic_id, first_visit_at, last_visit_at, status | Many-to-many · audit trail |
consent_records | patient_id, scope (allergies/meds/chronic/encounters/labs/imaging), granted_to_clinic_id, granted_at, expires_at, withdrawn_at, withdraw_reason | Per-clinic per-scope |
cross_tenant_access_log | accessor_user_id, accessor_clinic_id, patient_id, source_clinic_id, accessed_at, fields_accessed[], purpose, consent_ref | Audit trail |
global_allergy_registry | patient_id, drug_class, severity, source_adr_id, registered_at, registered_by_clinic, status | FR-8.4 always-share safety |
fhir_export_jobs | id, patient_id, requested_at, completed_at, bundle_url, expires_at | FR-8.8 self-portability |
5a. PostgreSQL Row-Level Security
-- RLS policy on encounters
CREATE POLICY encounter_cross_tenant_select ON encounters FOR SELECT
USING (
-- Same tenant
tenant_id = current_setting('app.current_tenant')::uuid
OR
-- Cross-tenant with consent
EXISTS (
SELECT 1 FROM consent_records cr
WHERE cr.patient_id = encounters.patient_id
AND cr.granted_to_clinic_id = current_setting('app.current_clinic')::uuid
AND cr.scope @> '["encounters"]'
AND (cr.expires_at IS NULL OR cr.expires_at > now())
AND cr.withdrawn_at IS NULL
)
);
6. 🔌 API + FHIR
POST /api/v1/cross-clinic/consent # patient grants
Body: { scope, target_clinic_id?, expires_at? }
DELETE /api/v1/cross-clinic/consent/{id} # patient withdraws
GET /api/v1/cross-clinic/patient/{patient_id}/timeline
# cross-clinic encounters chronological (gated by consent)
GET /api/v1/cross-clinic/patient/{patient_id}/allergies
# global registry · always accessible (safety override)
POST /api/v1/cross-clinic/fhir/export # FR-8.8 patient request
Returns: { job_id, status }
GET /api/v1/cross-clinic/fhir/export/{job_id}/bundle
# FHIR R4 Bundle ZIP download
GET /api/v1/cross-clinic/audit/access-log/{patient_id}
# cross-tenant access trail
# FHIR R4 Resources supported
- Patient
- Encounter
- Observation (vitals · labs)
- Condition (chronic · current dx)
- MedicationRequest (RX)
- MedicationStatement (current meds)
- AllergyIntolerance
- Procedure
- DocumentReference (SOAP notes · referrals)
# Endpoints (FHIR-compliant)
GET /fhir/Patient/{id}
GET /fhir/Encounter?patient={id}
GET /fhir/Observation?patient={id}&category=vital-signs
GET /fhir/MedicationRequest?patient={id}
POST /fhir/Bundle (search bundle export)
7. 🔁 State Machine
Patient consent lifecycle:
PROMPT_CONSENT (at first cross-clinic access)
│
├──► GRANTED (with scope) ──► ACTIVE ──┬──► EXPIRED
│ │
│ └──► WITHDRAWN ──► CASCADE_HIDE
│
└──► DENIED ──► RESTRICTED (allergy-only safety override active)
Cross-tenant query:
REQUEST ──► CHECK_CONSENT ──┬──► ALLOWED ──► AUDIT_LOG ──► RESPONSE
│
└──► DENIED ──► AUDIT_LOG ──► 403
8. 🤖 Agent Specification
M8 mostly enforcement + FHIR mapping. LLM untuk: (a) consent UI explanation BM/EN, (b) cross-clinic timeline summarisation, (c) FHIR Bundle metadata description.
- Model: Llama 8B (mostly rule + integration)
- Memory: consent records cache · FHIR resource templates
- Guardrails: RLS enforcement (DB layer) · consent gate (API layer) · audit per access · safety override allergy/ADR always-share
9. 🎨 UI/UX
- Patient PWA: Privacy Settings page · consent toggles per data category · scope per clinic · withdraw button · cross-clinic visit history
- Doctor (M4 enhanced): Cross-clinic timeline tab · source clinic indicator · "consent granted" badge · access audit visible
- Pharmacy (M5 enhanced): Global allergy alerts always visible
- FHIR export UI: Patient self-service · "Download my health record" · ZIP download · expiry notification
- Klinik admin: Inbound referral inbox · cross-clinic patient roster (with consent) · network analytics
10. ✔️ Acceptance Criteria
- AC-8.1: Cross-tenant query gated by consent · 0 unauthorized access on 30 simulated attempts
- AC-8.2: Allergy registry propagation <30s across all clinics in network
- AC-8.3: Consent withdraw cascade hide future access <5min
- AC-8.4: FHIR R4 Bundle export validates against HL7 spec · 95%+ resource coverage
- AC-8.5: Audit log captures every cross-tenant access · queryable per-patient · MOH-ready format
- AC-8.6: Patient consent UI · BM/EN clear explanation · opt-in per scope
- AC-8.7: Cross-clinic timeline render <1s for patient with 20+ encounters
- AC-8.8: Network value demonstrable: 3-clinic test scenario · meaningful continuity in pilot
- AC-8.9: Data residency 100% Malaysia · audit verifies no offshore
11. 🧪 Test Plan
| Tier | Cases | Coverage |
|---|---|---|
| Unit | Consent gate logic · scope match · withdraw cascade · FHIR resource mapper | ≥85% |
| Integration | End-to-end cross-tenant query · consent grant + use + withdraw | 100% paths |
| Security | 30 unauthorized cross-tenant attempts · RLS bypass · token reuse · 0 success expected | 0 leak |
| FHIR conformance | HL7 FHIR R4 validator · all 9 resources tested | ≥95% pass |
| Privacy | DSAR (M9) integration · consent withdraw cascade tested | 30-day SLA met |
| Network | 3-klinik simulation · patient cross-visit · timeline accuracy | UAT pass |
12. 🔗 Dependencies
- Hard: M9 (multi-tenant audit · consent enforcement · per-action gate · RBAC) · 11-section Patient model
- Soft: M1, M4, M5, M6, M7 (semua module yg generate cross-clinic relevant data)
- External: FHIR R4 server (HAPI atau Smile CDR) untuk standardised exchange · MOH MyHEALTH portal interop
- Network condition: ≥3 klinik join + 1 hospital reference site for meaningful value
13. 🏃 Sprint Allocation
- Day 1-2: Patient master cross-tenant model · global MRN · RLS policies
- Day 3-4: Consent records · scope matrix · per-action gate
- Day 5-6: Cross-tenant query · API gating · audit log
- Day 7-8: Global allergy registry · always-share safety override
- Day 9-10: FHIR R4 Bundle export · 9 resource mapping
- Day 11-12: Patient consent UI · withdraw cascade · timeline render
- Day 13: 3-klinik simulation E2E · security testing
- Day 14: Sprint review · demo
14. ⚠️ Module-Specific Risks
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Consent fatigue (patient skip · grant all) | Med | 🟠 PDPA spirit violation | Sensible defaults · just-in-time consent prompt · clear language · review period |
| Cross-clinic data leak (RLS bypass) | Low | 🔴 PDPA breach | Multi-layer enforcement (DB RLS + API gate + audit) · per-PR security review · pen-test |
| Klinik competitive concern (poach patients) | Med | 🟠 Adoption resistance | Network agreement · non-poach clauses · patient-driven choice (klinik can't block) |
| FHIR conformance gap | Med | 🟢 Interop limit | HAPI validator · MOH MyHEALTH compatibility test · iterate |
| Data residency leak (offshore cloud burst) | Low | 🔴 PDPA | Cloud burst gated · MY-region only LLM · fallback to on-prem · audit verifies |
| Withdraw cascade incomplete (data linger) | Low | 🟠 Right-to-erasure | Cascade test · audit hide vs delete distinction · soft-delete only · MOH retention exception |