Architectural Decisions Register
Key architectural decisions for REV.health, including IC rule deviations, rationale, and consequences.
DEC-RH-001: Users & Patients Are Global (No OrganizationID)
Choice: User and Patient entities have no OrganizationID column. They exist once globally across the platform.
Rationale: A patient is a person, not an organizational asset. Multi-org patients (e.g., referred between practices) must not be duplicated. This matches real-world identity: one person, one record, many care relationships.
Affected Modules: All modules — every module references User/Patient.
Consequences:
- Org-scoping must be enforced at the relationship level (CareRelationship, PatientOrgMembership), not the entity level.
- Query patterns must include explicit org-scope filters via joins rather than partition-key isolation on User/Patient.
- Patient data portability is native — no migration required when a patient moves between orgs.
DEC-RH-002: Clinical Facts Are Global with SourceOrganizationID
Choice: Clinical facts (Allergy, Medication, Condition, Vital, LabResult, Imaging, Immunization, CarePlan, Surgery, Problem, Diagnosis, Procedure, Prescription, PatientDocument) are global entities partitioned by PatientID with a SourceOrganizationID for provenance.
Rationale: Clinical facts belong to the patient, not the org. A medication prescribed by Org A is still the patient's medication when seen at Org B. SourceOrganizationID preserves the chain of custody without org-locking the data.
Affected Modules: Clinical Documentation, Labs, Imaging, eRx, Patient Portal, Referrals.
Consequences:
- Read-access audit must track which org is viewing another org's sourced data.
- Write permissions are scoped: only the source org (or the patient) can modify; other orgs get read-only access.
- TEFCA/HIE data flows map naturally — incoming records get SourceOrganizationID of the external org.
DEC-RH-003: Short GUID Encoding (Base62, 22 chars)
Choice: All primary keys are UUID v4 values encoded as 22-character Base62 strings. The encoding is deterministic and reversible.
Rationale: Standard UUIDs (36 chars with hyphens) are verbose in URLs, JSON payloads, and Cosmos partition keys. Base62 is URL-safe, case-sensitive, and compresses 128 bits into 22 characters with no information loss.
Affected Modules: All modules (every entity uses ShortGUID PKs).
Consequences:
- All API consumers must implement Base62 encode/decode (reference implementations provided in JS, C#, Python).
- Database queries use the encoded form — no runtime conversion overhead.
- Cosmos partition keys are shorter, improving throughput and reducing RU cost.
DEC-RH-004: Read-Access Audit with Authorization Chains
Choice: Every clinical data read generates a hash-chained audit entry that records the full authorization chain (CareOrgMember, FamilyProxy, ExternalSpecialist, TEFCA, BreakTheGlass, Researcher).
Rationale: HIPAA requires accounting of disclosures. Patients need to see who accessed their data and why. Hash-chaining prevents tampering. The authorization chain captures the legal basis for access, not just the actor.
Affected Modules: All modules with clinical data access, Patient Portal (patient-facing audit view).
Consequences:
- Read latency increases by ~2ms per audited read (async write to audit log).
- Audit storage grows proportionally with read volume — requires lifecycle policies and archival to S3 Object Lock.
- Patients can view their own access log via the Patient Portal.
DEC-RH-005: Patient = User Identity (PatientID = UserID)
Choice: PatientID is a foreign key to User.UserID with the same value. Every patient is a user; there is no separate identity for patients vs. staff vs. providers — they are all Users with different roles.
Rationale: Clinicians are also patients. A single identity model eliminates duplicate records, simplifies SSO, and enables a provider to view their own patient record with the same Patient Portal experience.
Affected Modules: IAM, Patient Portal, all clinical modules.
Consequences:
- Role-based access is the only distinction between a "patient user" and a "clinician user."
- Patient self-service (portal, mobile app) uses the same auth flow as clinician login.
- De-identification for research must handle the fact that a single UserID may appear in both staff and patient contexts.
DEC-RH-006: PostgreSQL as Primary OLTP
Choice: Use PostgreSQL (Azure Database for PostgreSQL Flexible Server) as the primary OLTP store with row-level security for multi-tenancy.
Rationale: Mature relational model, strong ACID guarantees, row-level security for tenant isolation, rich ecosystem.
Affected Modules: All modules (data layer).
Consequences:
- Superseded — replaced by Cosmos DB (DEC-RH-008) due to global distribution requirements, elastic throughput needs, and native multi-tenancy via partition keys.
DEC-RH-007: Read-Access Audit Scoped to Patient Across Orgs
Choice: Audit log entries are partitioned by PatientID, not OrganizationID. This means a patient's complete access history is queryable in one partition regardless of which org performed the access.
Rationale: The patient is the data subject. Regulatory queries ("who accessed my data?") are patient-centric. Partitioning by patient makes these queries single-partition reads in Cosmos, which is fast and cheap.
Affected Modules: Audit subsystem, Patient Portal, compliance reporting.
Consequences:
- Org-centric audit queries ("all access by my staff") require a cross-partition query or a secondary index on ActorUserID/OrganizationID.
- Audit volume per patient partition grows with the number of orgs accessing that patient's data.
DEC-RH-008: Azure Cosmos DB as Primary OLTP
Choice: Use Azure Cosmos DB (NoSQL API) as the primary OLTP store. Multi-tenancy is achieved via partition keys (T1), dedicated databases (T2), or dedicated accounts (T3).
Rationale: Cosmos provides single-digit-ms latency at any scale, native partition-key isolation, tunable consistency, global distribution for multi-region DR, and a 99.999% SLA. Healthcare workloads need elastic throughput (flu season spikes) and geographic compliance (data residency).
Affected Modules: All modules (data layer).
Consequences:
- Schema design shifts from relational normalization to document-oriented modeling with denormalization.
- Cross-partition queries are expensive — data access patterns must be designed around partition keys.
- Transactions are limited to single logical partitions (transactional batch).
Global Containers
| Container | Partition Key | Primary Entities |
|---|---|---|
| global-patient | /PatientID | Patient, Allergy, Medication, Condition, Vital, LabResult, Imaging, Immunization, CarePlan, FamilyHistory, Surgery, Problem, ProblemList, Diagnosis, Procedure, Prescription, PatientDocument, SensitiveClassGating |
| global-provider | /ProviderID | Provider, Credential, Specialty, License |
| global-practice | /PracticeID | Practice, Location, Department |
| global-pharmacy | /PharmacyID | Pharmacy, PharmacyNetwork |
| global-specialty | /SpecialtyCode | Specialty taxonomy, sub-specialties |
| global-user | /UserID | User, UserRole, UserPreference |
| global-clinical-facts | /PatientID | Cross-org clinical fact aggregation view |
Org-Scoped Containers
| Container | Partition Key | Primary Entities |
|---|---|---|
| org-encounter | /OrganizationID | Encounter, CareRelationship, PatientOrgMembership |
| org-order | /OrganizationID | OrderSet, LabOrder, ImagingOrder, RxOrder |
| org-reminder | /OrganizationID | Reminder, RecallList, OutreachCampaign |
| org-referral | /OrganizationID | Referral, ReferralResponse |
| org-message | /OrganizationID | PortalMessage, InternalMessage |
| org-external-interop | /OrganizationID | TEFCA queries, HIE documents, C-CDA imports |
| org-rules | /OrganizationID | CdsRule, CdsHookEvent, AlertRule |
| org-audit | /PatientID | AuditLog (partitioned by patient within org context) |
| org-eligibility | /OrganizationID | EligibilityCheck, BenefitDetail, PriorAuth, Coverage |
| org-scheduling | /OrganizationID | Schedule, Slot, Appointment, WaitlistEntry, VisitType, VisitStage |
| org-rcm | /OrganizationID | Claim, ClaimLine, Remittance, Denial, PatientStatement, Payment |
| org-task | /OrganizationID | Task, WorkTask |
| org-payer-opt | /OrganizationID | PayerRule, FormularyCheck, ControlledSubstanceAuth, PdmpQuery |