Patient Chart Service — Service Overview
Status: populated
Owner: TBD
Last updated: 2026-04-17
Companion: Service Template · 03 platform-services · 02 DDD · FHIR-First
1. Purpose
The patient-chart-service is the authoritative longitudinal chart aggregate for the Ghasi-eHealth platform. It owns the structured clinical facts that describe a patient over time — the problem list, allergies/intolerances, vital signs, clinical notes, and the chart composition/sections that clinicians read and write from the bedside, clinic, ward, or virtual-care surface.
Unlike a read-only aggregator, this service is the source of truth for these clinical aggregates. It writes Condition, AllergyIntolerance, Observation (vital-signs), Composition, and DocumentReference FHIR resources; it emits a full domain event stream on every mutation; and it exposes both a product REST surface (/v1/chart/*, /v1/problems/*, /v1/allergies/*, /v1/vitals/*, /v1/clinical-notes/*) and a FHIR R4 read surface via interop-service.
2. Bounded Context
Patient Chart (previously scattered across five module specs: patient-chart, problem-list, allergies, vitals, clinical-notes) — classified as Core (mission-critical clinical domain). Consolidating the five modules into one service eliminates chart-shaped data being split across five datastores, each with its own RLS rules and outbox.
| Trait | Value |
|---|
| DDD classification | Core |
| Hexagonal layout | domain/ application/ infrastructure/ presentation/ |
| FHIR ownership | Condition, AllergyIntolerance, Observation (vital-signs), Composition, DocumentReference for chart documents, optional Provenance |
| Runtime | NestJS 11 / Node 22 / TS 5.x |
| Persistence | Postgres 16 with RLS; one logical schema patient_chart |
| Events | NATS JetStream — stream PATIENT_CHART |
3. Responsibilities
| Area | What Patient Chart Owns |
|---|
| Chart header | Banner composition (demographics proxy + alerts); encounter context mode (chart-only vs encounter-scoped) |
| Chart summary | Summary widget composition over owned aggregates + remote reads from medication-service, laboratory-service, radiology-service, immunizations-service, care-plan-service |
| Chart timeline | Longitudinal timeline with filters; cursor-paginated merge over owned aggregates + downstream reads |
| Problem list | Full lifecycle: active/inactive/resolved/entered-in-error; ICD-10/11 + SNOMED CT coding via terminology-service |
| Allergies/intolerances | Substance coding; reaction manifestations; severity; NKA/NKDA assertions; advisory safety check API for medication-service/orders-service |
| Vital signs | LOINC-coded Observation panels with UCUM units; range validation; abnormal flagging; trending |
| Clinical notes | Templated note authoring (SOAP, H&P, consult, procedure, discharge); draft → signed → amended lifecycle; cosign/attestation routing; AI-assist provenance capture |
| Chart snapshot | Handoff/rounding snapshot composition; export/print with audit and permission gate |
| Break-glass | Reason-captured emergency access with audit event emission |
| Chart access audit | Per-item access logging to audit-service |
4. Non-Responsibilities
| Area | Owner | Why Not Patient Chart |
|---|
| Patient demographics & MRN | registration-service | Chart reads via remote query; registration is source of truth |
| Encounters | registration-service | Chart consumes Encounter; does not mint |
| Medications | medication-service | Chart displays summary widget; medication-service owns MedicationRequest/MedicationAdministration |
| Orders / CPOE | orders-service | Chart links to orders; orders-service writes ServiceRequest |
| Lab results | laboratory-service | Chart displays results widget; LIS owns DiagnosticReport + lab Observation |
| Imaging reports | radiology-service | Chart displays filed imaging summaries (via DiagnosticReport read) |
| Immunizations | immunizations-service | Chart displays immunization widget |
| Care plans | care-plan-service | Chart displays active plans/goals widget |
| Document bytes (Binary) | document-service | Clinical-note PDFs referenced via DocumentReference → Binary in DMS |
| Terminology lookup | terminology-service | Chart consumes code lookup, value-set expansion, ICD/SNOMED search |
| Authorization | identity + tenant services | Chart calls access-policy checks; does not store roles |
5. Dependencies
5.1 Upstream (Chart calls out)
| Dependency | Pattern | Purpose |
|---|
| registration-service | Sync REST + registration.patient.* event consumer | Patient exists check; encounter context resolution |
| terminology-service | Sync REST (/internal/terminology/lookup, /validate, /expand) | Coded value rendering, value-set constraint, ICD/SNOMED search |
| provider-directory-service | Sync REST | Practitioner / PractitionerRole display resolution for performer, author, cosigner |
| facility-service | Sync REST | Location resolution for collection-location / encounter location |
| medication-service | Sync REST / event consumer | Summary widget data; allergy-check consumer on its side |
| laboratory-service | Sync REST | Summary & timeline widgets (DiagnosticReport, lab Observation) |
| radiology-service | Sync REST | Summary widget — filed imaging summaries |
| immunizations-service | Sync REST | Immunization widget |
| care-plan-service | Sync REST | Active care-plan widget |
| audit-service | Event producer | Every mutation + chart access emits audit |
| ai-gateway-service | Sync REST (only via Kong /v1/ai/*) | Clinical-note AI assist (Tier A) with HITL accept |
| document-service | Sync REST | DocumentReference + Binary for signed-note PDFs and scanned attachments |
5.2 Downstream (consumers)
| Consumer | Pattern | What They Consume |
|---|
| medication-service | Event consumer | patient_chart.allergy.added.v1, ...updated.v1 — refresh allergy safety-check cache |
| orders-service | Sync REST GET /v1/allergies/advisory?patientId= | CDS allergy check at order entry |
| population-health-service | Event consumer | Problem-list and vitals events for cohort builders / HMIS indicator exports |
| patient-portal-service | Sync REST (scoped) + event consumer | Patient-visible chart read |
| interop-service (FHIR gateway) | Sync REST /fhir/R4/* | Partner read/search for Condition, AllergyIntolerance, Observation, Composition, DocumentReference |
| communication-service | Event consumer | Cosign-requested routing, abnormal-vitals notifications |
5.3 Events consumed by Patient Chart
| Event | Producer | Purpose |
|---|
registration.patient.merged.v1 | registration-service | Re-home chart aggregates to surviving patient id |
registration.encounter.created.v1 | registration-service | Pre-fetch encounter cache for chart banner |
gdpr.subject_request.received.v1 | platform | Participate in GDPR erasure saga — redact author PII on historical records, retain clinical content per retention policy |
6. Slice Involvement
| Slice | Scope | Milestone |
|---|
| S0 — Core Baseline | Problem list CRUD + lifecycle; allergy CRUD + advisory API; vitals capture + trending; draft note + sign; chart summary (owned aggregates); banner | M0 |
| S1 — Integration + Offline | Medication/lab/imaging summary widgets; timeline cursor API; offline capture queue for vitals + allergies on tablet devices | M1 |
| S2 — Handoff + Sensitive | Chart snapshot export with audit; break-glass; sensitive-segment policy | M2 |
| S3 — AI-Assist + Advanced Notes | Clinical-notes AI assist (Tier A: template fill, summarize, STT from virtual-care), cosign routing, amendments with provenance | M3 |
7. Architectural Freeze Points
| Freeze | What Is Frozen | Frozen By |
|---|
| F03 | Branded IDs: ProblemId (prb_*), AllergyId (alg_*), VitalsSetId (vit_*), ObservationId (obs_*), NoteId (note_*), ChartId (cha_*) | M0 |
| F01 | EventEnvelope — all chart events conform to platform envelope | M0 |
| F05 | Allergy advisory payload shape — orders-service and medication-service contract | End of M1 |
8. Service Readiness Levels
| Level | Description | Target |
|---|
| L2 | Core aggregate CRUD live, FHIR read surface on Condition/AllergyIntolerance/Observation available | M0 |
| L3 | Full summary + timeline, contract tests against medication/lab/rad/imm/care-plan | M1 |
| L4 | Break-glass + snapshot + AI-assist + SLO-governed, chaos-tested | M3 |
9. Architecture Diagram
10. Key Design Decisions
- Five modules consolidated into one service. Legacy
patient-chart, problem-list, allergies, vitals, clinical-notes ship as a single bounded context because the aggregates share the same tenancy, RLS model, audit path, and consumer set. Cross-aggregate invariants (e.g., "NKA blocks adding a substance allergy", "signed notes can reference active problems atomically") are enforced in-process.
- FHIR is canonical meaning; Postgres is authoritative state. Writes go to REST on
/v1/*; FHIR read/search is composed by adapters in infrastructure/fhir/. Writes via FHIR HTTP are out-of-scope until an explicit release declares them (per FHIR_FIRST_STANDARD.md §6).
- Clinical notes use Composition + DocumentReference. Draft and signed notes are structured Composition resources with sections; on sign, a rendered PDF is uploaded to document-service and referenced by
DocumentReference. Legal record tracking lives on the ClinicalNote aggregate; PDF bytes live in document-service.
- Allergy advisory is a synchronous internal API. orders-service and medication-service call
GET /v1/allergies/advisory?patientId=&rxnorm= at order entry / prescribe sign. Fail-open is enforced by callers, not by the chart service.
- Vitals are one Observation per measurement, grouped by a
vitals_set_id. Panel pattern supported for list/read; storage is per-measurement to preserve FHIR fidelity.
- AI assist writes a
NoteAIProvenance row on accept. No AI output ever replaces signed content without explicit clinician "accept" action.
- Summary widgets are composed, not duplicated. Chart does not shadow-store medications or lab results; it calls downstream read APIs with per-user scope.
11. Source reconciliation (five-module merge decisions)
| Decision | Rationale |
|---|
Single patient_chart DB schema with sub-folders problems/, allergies/, vitals/, notes/, chart/. | One RLS policy, one outbox, one migration pipeline. |
Unified event stream PATIENT_CHART with subject pattern patient_chart.{aggregate}.{event}.v{N}. Subjects preserved: patient_chart.problem.added.v1, patient_chart.allergy.added.v1, patient_chart.vitals.recorded.v1, patient_chart.note.signed.v1, patient_chart.breakglass.invoked.v1. | Legacy subjects (PROBLEMS.*, ALLERGIES.*, VITALS.*, CLINICAL_NOTES.*) are deprecated and dual-published for M0→M1; see MIGRATION_PLAN.md. |
Unified FR-CHART-* prefix for synthesized FRs in this service. Legacy FR refs (FR-PCHART-*, FR-PROB-*, FR-ALG-*, FR-VIT-*, FR-NOTES-*) preserved in the "Legacy ref" column of EPICS.md and USER_STORIES.md. | Single FR domain for the merged service. |
| Banner + Summary + Timeline remain part of this service rather than being extracted as a read-only aggregator. | Chart-shaped composition requires access to owned aggregates' RLS; keeping the composition in-process avoids double-RLS and latency. |
UX_UI_SPEC (from _sources/patient-chart/UX_UI_SPEC.md, ~595 lines) is treated as informative for the UI layer in the patient-portal-service and provider-web app; the backend service offers the API surface required to satisfy it. | UI belongs to the app package, not the service. |
STT APSO integration (_sources/clinical-notes/STT_APSO_INTEGRATION.md) is delegated to ai-gateway-service. Chart consumes finalized STT drafts via the standard AI-assist contract. | Keep AI plumbing centralized. |
requisitions-referrals touches (cross-links from clinical-notes) are redirected to orders-service per ADR-0046. | Retired module. |