Registration Service — Domain Model
Status: populated Owner: TBD Last updated: 2026-04-17 Companion: Service Template · 03 platform-services · 02 DDD
1. Aggregates
1.1 PatientAggregate
Root aggregate for person identity and demographics in a tenant.
Invariants
| # | Invariant |
|---|---|
| I-PAT-01 | Identifiers are unique per (tenantId, system, value) — no two patients share the same identifier in the same assigning authority. |
| I-PAT-02 | A merged patient (mergedIntoPatientId ≠ null) is inactive and cannot be updated. |
| I-PAT-03 | deceasedDateTime may only be set when deceased = true. |
| I-PAT-04 | isProvisional and isUnidentified are mutually exclusive. |
| I-PAT-05 | isNewborn = true requires newbornLinkedPatientId or a MOTHER/GUARDIAN NOK row. |
| I-PAT-06 | preferredLang must be in the allowed language set. |
| I-PAT-07 | MPI score ≥ 85 blocks normal create (409) unless isUnidentified = true. |
| I-PAT-08 | Optimistic lock version must match on every mutation. |
State Machine
1.2 EncounterAggregate
Single visit registration linked to a Patient.
Invariants
| # | Invariant |
|---|---|
| I-ENC-01 | Status transitions follow allowed graph only. |
| I-ENC-02 | admittedAt is set once on first in-progress transition. |
| I-ENC-03 | dischargedAt is set once on finished transition. |
| I-ENC-04 | patientId references an active, non-merged patient in the same tenant. |
Encounter Status Machine
2. Entities
| Entity | Aggregate | Key attributes |
|---|---|---|
PatientName | Patient | use, family, given[], script, textDirection, patronymic, middle |
PatientIdentifier | Patient | system, value, typeCode, issuer, verificationStatus, confidence |
PatientTelecom | Patient | system, value, use, rank |
PatientAddress | Patient | country, stateProvince, district, city, line[], postalCode, use |
NextOfKinEntry | Patient | relationship (coded), name, phone, relatedPatientId?, priority |
ConsentFlag | Patient | type, granted, channel |
PatientPortrait | Patient | contentType, consentGiven, clinicalDisplayAllowed, retentionUntil, supersededPortraitId? |
PatientExtensionInstance | Patient | bundleKey, schemaId, payload (JSONB), effectiveFrom, effectiveTo? |
VitalStatusCorrection | Patient | priorDeceased, priorDeceasedDateTime, newDeceased, correctedBy, correctedAt |
3. Value Objects
| Value Object | Type / Format |
|---|---|
PatientId | Branded<string, 'PatientId'> — UUID |
EncounterId | Branded<string, 'EncounterId'> — UUID |
MRN | PAT-{tenantCode}-{timestamp} |
TemporaryEnterpriseKey | UNK-{tenantCode}-{timestamp}-{random8} |
NationalIdentifierValue | Validated 8–20 digit string |
MPIScore | Integer 0–100; ≥85 → probable duplicate; 71–84 → weak match |
KinRelationshipCode | Enum from kin_relationship_types catalog |
LanguageCode | en | ps | ps-af | fa | fa-af | ar | ar-ae | ar-af | ar-sa |
ScriptCode | latn | arab | arab-af | fa-af | ps |
EncounterStatus | planned | arrived | in-progress | finished | cancelled |
AlternateIdentifierEvidence | {system, value, typeCode, issuer, verificationStatus, confidence} |
4. Domain Events
| NATS subject | CloudEvents type | Trigger | Version |
|---|---|---|---|
REGISTRATION.patient.created | ghasi.registration.patient.created | Patient creation succeeds | v1 |
REGISTRATION.patient.updated | ghasi.registration.patient.updated | Demographics/identifiers updated | v1 |
REGISTRATION.patient.vital-status-changed | ghasi.registration.patient.vital-status-changed | Deceased recorded or corrected | v1 |
REGISTRATION.patient.duplicate-detected | ghasi.registration.patient.duplicate-detected | MPI ≥ 85 aborts create | v1 |
REGISTRATION.patient.unidentified-duplicate-review | ghasi.registration.patient.unidentified-duplicate-review | Unidentified create + weak match | v1 |
REGISTRATION.patient.merged | ghasi.registration.patient.merged | Merge executes (source deactivated) | v1 |
REGISTRATION.patient.unmerged | ghasi.registration.patient.unmerged | Source reactivated via unmerge | v1 |
REGISTRATION.encounter.registered | ghasi.registration.encounter.registered | Encounter created | v1 |
REGISTRATION.encounter.status-changed | ghasi.registration.encounter.status-changed | Encounter status transitions | v1 |
5. Ubiquitous Language
| Term | Definition |
|---|---|
| Patient | Registered person in the platform identity system |
| MRN | Medical Record Number — tenant-scoped, system-generated |
| MPI | Master Patient Index — probabilistic matching engine |
| Merge | Deactivating a duplicate (source) patient and transferring identifiers to the survivor |
| Survivor | The patient identity that remains active post-merge |
| Source | The duplicate deactivated during merge |
| Provisional patient | Minimal identity for emergency intake pending full confirmation |
| Unidentified patient | Unknown-person record (no name/DOB); must be reconciled |
| Reconciliation | Linking or merging provisional/unidentified chart to identified patient |
| Vital status | Living or deceased state, with optional time of death |
| Next-of-kin (NOK) | Related person (family/guardian/emergency contact) linked to patient |
| NID / Tazkira | Afghanistan national identity document types |
| Alternate identity evidence | Non-NID credentials for populations without national IDs |
| stablePatientId | Platform-wide stable UUID duplicated in all events |
| Encounter | Single visit registration (check-in through discharge) |
| Break-glass | Emergency access beyond normal role scope with mandatory justification |
| Relationship graph | Network of patient–NOK and patient–patient links |