Architecture Review: Healthcare Records Platform
An electronic health record (EHR) architecture built around strict auditability, HIPAA compliance, and append-only correctness. Clinical records are mutable by design (amendments, addenda) but corrections must be explicitly attributed, not silently overwritten. Event sourcing provides a reconstructable audit log; PostgreSQL row-level security enforces patient-level access control at the database layer; Kafka streams HL7 FHIR events to downstream clinical systems. The architecture must support breach detection, access auditing, and state reconstruction at any historical point: not just current state retrieval.
Evidence Confidence
Moderate
strong
Executive Summary
Healthcare Records Platform: moderate operational readiness (81% evidence confidence). 0 architectural strengths identified, 6 operational risks to manage. Primary concern: Lock Contention. Requires Advanced operational maturity.
Readiness Rationale
Overall moderate readiness across 8 dimensions. Weak: consistency. Limited: team maturity. Strong: migration, observability, failure recovery.
Key Concerns
- !Lock Contention
- !Replication Lag Cascade
Key Strengths
- +Architecture is well-defined for the financial ledger problem profile
8
Assessments
2
Tradeoffs
6
Sections
11
Recommendations
Readiness Assessments
8Architectural Tradeoffs
2Recommendations
11Monitor: Replication Lag Cascade
risk_monitoringAsynchronous replicas fall behind the primary under write load and serve reads from an older version of the data. Reads keep succeeding, so nothing errors; what breaks is one of three specific consistency guarantees (read-after-write, monotonic reads, or consistent prefix), each with a distinct user-visible anomaly.
Affects 1 node. (Read Replica)
Monitor: Lock Contention
risk_monitoringConcurrent writers to the same rows serialize behind each other's row locks, so latency is set not by the work a transaction does but by how long it waits for the writers ahead of it. On a hot row the queue depth, and therefore the tail latency, grows with concurrency while throughput flattens. Blocked writers hold connections open, so a single contended row can drain the connection pool as a secondary failure.
Affects 1 node. (Write-Heavy Transactional)
Implement: Monitor replication lag signals
observabilitySeed 'Replication Lag Under Write Burst' identifies 4 metrics relevant to replication_lag_cascade. Execution preview confirms this risk manifests under modelled load.
Metrics to instrument: replication_lag_seconds, stale_read_rate, replica_wal_apply_rate
Mutable clinical records with application-layer audit logging → Event-sourced clinical records with atomic audit event + outbox writes
migration_planningTrigger: HIPAA audit requirement exposed during external security review; inability to reconstruct which practitioner accessed a patient record and when; audit log gaps found during incident investigation (application-layer logging not guaranteed to capture all access paths, including background jobs and admin tools). Migrate from 'Mutable clinical records with application-layer audit logging' to 'Event-sourced clinical records with atomic audit event + outbox writes'. Define a "record_state_bootstrap" event type for all existing clinical records as of the migration cutover date. These events carry the current state with a note that pre-migration history is unavailable. This satisfies HIPAA reconstruction requirements for post-migration access while being honest about pre-migration gaps.
Historical records before the migration cutover cannot be event-sourced retroactively without synthetic "initial_state" events: document the boundary date explicitly and include it in audit reports; The transition requires a period of dual-write (old mutable path + new event path) with reconciliation to validate equivalence before decommissioning the mutable-only path
Inline Kafka publish inside clinical transaction (dual-write) → Outbox pattern with CDC relay for FHIR event delivery
migration_planningTrigger: FHIR events being published to Kafka but corresponding clinical record transactions rolling back, resulting in phantom events being consumed by downstream clinical systems; or Kafka publish failures causing clinical transactions to roll back and block charting workflows. Migrate from 'Inline Kafka publish inside clinical transaction (dual-write)' to 'Outbox pattern with CDC relay for FHIR event delivery'. The outbox pattern is non-negotiable for FHIR delivery. The dual-write pattern creates phantom clinical events when transactions roll back: in a healthcare context, a phantom "medication_administered" event on a downstream system is a patient safety incident. The outbox guarantees at-least-once delivery aligned with transaction commit; downstream consumers must dedupe on event ID so replays do not create duplicate clinical events.
Outbox relay introduces delivery lag (< 5s under normal load): downstream systems must tolerate this latency and must not assert synchronous availability of FHIR events as part of the clinical transaction commit path; FHIR message construction errors in the relay must dead-letter and alert rather than silently dropping: a lost FHIR event can mean a downstream system has no record of a clinical event
Prepare runbook for: Burst Traffic Cold Cache Stampede
simulation_preparednessSimulation demonstrates critical degradation of redis, postgresql
Without a runbook, recovery from this failure mode will be ad-hoc
Prepare runbook for: Connection Pool Exhaustion with Horizontal User Scale
simulation_preparednessSimulation demonstrates critical degradation of postgresql
Without a runbook, recovery from this failure mode will be ad-hoc
Plan evolution: OLTP Analytics Queries → OLTP + OLAP Separation
evolution_planningEvolution from Unified OLTP + Analytics on PostgreSQL → Separated OLTP (PostgreSQL) + OLAP (ClickHouse/Snowflake)
Migration complexity: medium. Rollback: always.
Plan evolution: Single Cache Layer → Distributed Cache
evolution_planningEvolution from Single Redis Node / Sentinel Cluster → Distributed Redis Cluster (Consistent Hash Ring)
Migration complexity: medium. Rollback: complex.
Monitor threshold: Tier 1: Audit Log Write Throughput
scaling_monitoringSignal: Audit log table growing at > 500K rows/day; INSERT p99 on audit_log > 20ms; autovacuum unable to keep up with dead tuple accumulation from UPDATE operations on the audit log's index pages
Bottleneck: Audit log receiving one row per record access creates I/O contention with clinical record writes on the same PostgreSQL primary. Evolution: Partition the audit_log table by month using PostgreSQL declarative partitioning; child partitions allow VACUUM to operate on bounded table segments without scanning the entire history; index each partition independently to keep index size proportional to partition row count rather than total log size
Monitor threshold: Tier 2: Concurrent Encounter Write Lock Contention
scaling_monitoringSignal: pg_locks showing RowExclusiveLock waits on clinical_records or encounter_notes during shift-change peak hours; write p99 > 100ms; occasional deadlock errors in application logs correlated with concurrent addenda writes to the same encounter
Bottleneck: Multiple clinical staff members writing addenda to the same encounter simultaneously, or two processes updating encounter status concurrently. Evolution: Implement optimistic locking with an encounter version column; reject concurrent writes with a conflict error and require the client to reload and retry; this eliminates lock waits by failing fast rather than waiting; ensure the application presents a clear conflict resolution UI: in a clinical context, silent overwrites of concurrent edits are a patient safety risk, not just a data integrity issue
Scaling Pressure Signals
8Audit log table growing at > 500K rows/day; INSERT p99 on audit_log > 20ms; autovacuum unable to keep up with dead tuple accumulation from UPDATE operations on the audit log's index pages
Threshold
Tier 1: Audit Log Write Throughput
Likely Bottleneck
Audit log receiving one row per record access creates I/O contention with clinical record writes on the same PostgreSQL primary
Recommended Evolution
Partition the audit_log table by month using PostgreSQL declarative partitioning; child partitions allow VACUUM to operate on bounded table segments without scanning the entire history; index each partition independently to keep index size proportional to partition row count rather than total log size
pg_locks showing RowExclusiveLock waits on clinical_records or encounter_notes during shift-change peak hours; write p99 > 100ms; occasional deadlock errors in application logs correlated with concurrent addenda writes to the same encounter
Threshold
Tier 2: Concurrent Encounter Write Lock Contention
Likely Bottleneck
Multiple clinical staff members writing addenda to the same encounter simultaneously, or two processes updating encounter status concurrently
Recommended Evolution
Implement optimistic locking with an encounter version column; reject concurrent writes with a conflict error and require the client to reload and retry; this eliminates lock waits by failing fast rather than waiting; ensure the application presents a clear conflict resolution UI: in a clinical context, silent overwrites of concurrent edits are a patient safety risk, not just a data integrity issue
Kafka consumer lag growing on FHIR event topics; downstream clinical systems reporting stale data; outbox table accumulating unprocessed rows > 10,000 at rest
Threshold
Tier 3: FHIR Event Streaming Throughput
Likely Bottleneck
FHIR message transformation and Kafka publish throughput falling behind clinical event write volume
Recommended Evolution
Increase outbox relay consumer parallelism; partition Kafka FHIR topics by patient_id to maintain per-patient event ordering while enabling parallel processing; profile FHIR message construction for CPU-intensive transformation paths (e.g., terminology code mapping) and consider caching terminology lookups in Redis
PostgreSQL primary I/O > 70% from read queries during morning rounds (when all staff are querying overnight encounter summaries simultaneously); read replica replication lag > 5s during peak read periods
Threshold
Tier 4: Multi-Facility Read Replica Distribution
Likely Bottleneck
Read-heavy clinical summary queries competing with event write volume on the shared primary
Recommended Evolution
Direct all clinical summary and dashboard reads to the read replica via CQRS routing; ensure read replica has synchronous_standby_names configured to receive writes at most 5 seconds behind primary; audit queries specifically should read from primary (not replica) to guarantee audit log completeness is not affected by replication lag
Audit log table growing at > 500K rows/day; INSERT p99 on audit_log > 20ms; autovacuum unable to keep up with dead tuple accumulation from UPDATE operations on the audit log's index pages
Threshold
Escalation trigger: Audit log receiving one row per record access creates I/O contention with clinical record writes on the same PostgreSQL primary
Likely Bottleneck
Tier 1: Audit Log Write Throughput
Recommended Evolution
Monitor: replication_lag_seconds, stale_read_rate, replica_wal_apply_rate
pg_locks showing RowExclusiveLock waits on clinical_records or encounter_notes during shift-change peak hours; write p99 > 100ms; occasional deadlock errors in application logs correlated with concurrent addenda writes to the same encounter
Threshold
Escalation trigger: Multiple clinical staff members writing addenda to the same encounter simultaneously, or two processes updating encounter status concurrently
Likely Bottleneck
Tier 2: Concurrent Encounter Write Lock Contention
Recommended Evolution
Monitor: replication_lag_seconds, stale_read_rate, replica_wal_apply_rate
Kafka consumer lag growing on FHIR event topics; downstream clinical systems reporting stale data; outbox table accumulating unprocessed rows > 10,000 at rest
Threshold
Escalation trigger: FHIR message transformation and Kafka publish throughput falling behind clinical event write volume
Likely Bottleneck
Tier 3: FHIR Event Streaming Throughput
Recommended Evolution
Monitor: replication_lag_seconds, stale_read_rate, replica_wal_apply_rate
PostgreSQL primary I/O > 70% from read queries during morning rounds (when all staff are querying overnight encounter summaries simultaneously); read replica replication lag > 5s during peak read periods
Threshold
Escalation trigger: Read-heavy clinical summary queries competing with event write volume on the shared primary
Likely Bottleneck
Tier 4: Multi-Facility Read Replica Distribution
Recommended Evolution
Monitor: replication_lag_seconds, stale_read_rate, replica_wal_apply_rate
Migration Readiness
12Migration Stages
3Mutable clinical records with application-layer audit logging → Event-sourced clinical records with atomic audit event + outbox writes
infoMigration trigger: HIPAA audit requirement exposed during external security review; inability to reconstruct which practitioner accessed a patient record and when; audit log gaps found during incident investigation (application-layer logging not guaranteed to capture all access paths, including background jobs and admin tools)
Inline Kafka publish inside clinical transaction (dual-write) → Outbox pattern with CDC relay for FHIR event delivery
infoMigration trigger: FHIR events being published to Kafka but corresponding clinical record transactions rolling back, resulting in phantom events being consumed by downstream clinical systems; or Kafka publish failures causing clinical transactions to roll back and block charting workflows
All facilities sharing a single PostgreSQL cluster → Per-facility database with cross-facility patient index and record linkage
infoMigration trigger: Facility acquisition or merger; compliance requirement for data residency (state or country-level); single-cluster I/O saturation as facility count grows beyond 5–10 concurrent clinical sites
Risks
9Historical records before the migration cutover cannot be ev
warningHistorical records before the migration cutover cannot be event-sourced retroactively without synthetic "initial_state" events: document the boundary date explicitly and include it in audit reports
The transition requires a period of dual-write (old mutable
warningThe transition requires a period of dual-write (old mutable path + new event path) with reconciliation to validate equivalence before decommissioning the mutable-only path
Outbox relay introduces delivery lag (< 5s under normal load
warningOutbox relay introduces delivery lag (< 5s under normal load): downstream systems must tolerate this latency and must not assert synchronous availability of FHIR events as part of the clinical transaction commit path
FHIR message construction errors in the relay must dead-lett
warningFHIR message construction errors in the relay must dead-letter and alert rather than silently dropping: a lost FHIR event can mean a downstream system has no record of a clinical event
Cross-facility patient record linkage is the highest-risk op
warningCross-facility patient record linkage is the highest-risk operation : an incorrect merge that combines two different patients' records under one identity is a critical patient safety incident requiring immediate rollback and incident reporting
Schema migrations must now be applied to N facility database
warningSchema migrations must now be applied to N facility databases with coordinated rollout: the migration tooling must be tested against the full fleet before any clinical migration window
Projection lag creates a read-after-write window where users
criticalProjection lag creates a read-after-write window where users see stale data after their own writes. Mitigation: Route immediate post-write reads to the write store (session-scoped write token); accept eventual consistency only for non-user-initiated reads
↗ direct-db-to-cqrs
Projection rebuild after schema change can take hours or day
criticalProjection rebuild after schema change can take hours or days on large datasets. Mitigation: Design blue/green projection deployment: build new projection in parallel before switching traffic; test rebuild time in staging
↗ direct-db-to-cqrs
Cross-service workflows that previously used database transa
criticalCross-service workflows that previously used database transactions now require Saga orchestration. Mitigation: Design idempotent event handlers; implement compensating transactions for every multi-step workflow; test failure injection in staging
↗ modular-monolith-to-event-driven
Review Sections
6Referenced Intelligence