SYSTEM_DESIGN
System Design: Drug Interaction Checker
Design a high-performance drug interaction checking system using pharmacological knowledge graphs, real-time clinical decision support, severity-ranked alerting, and integration with EHR prescribing workflows to prevent adverse drug events.
Requirements
Functional Requirements:
- Check drug-drug interactions (DDIs) for a medication against a patient's complete active medication list in real-time during prescribing
- Check drug-allergy interactions using patient allergy records and cross-reactivity knowledge (e.g., penicillin class cross-reactivity)
- Check drug-disease contraindications (e.g., metformin contraindicated in severe renal impairment)
- Severity-ranked alerting: categorize interactions as contraindicated, severe, moderate, or minor with clinical significance descriptions
- Support for duplicate therapy detection (two drugs from the same therapeutic class)
- Pharmacogenomic interaction alerts when genetic test results are available (e.g., CYP2D6 poor metabolizer affecting codeine metabolism)
Non-Functional Requirements:
- Respond within 200ms for single drug interaction checks (inline in the prescribing workflow)
- Handle 50,000 interaction checks/minute during peak prescribing hours (10 AM - 12 PM)
- Knowledge base updated weekly from FDA drug label changes, published literature, and pharmacological databases
- HIPAA-compliant: patient medication data encrypted at rest (AES-256) and in transit (TLS 1.3)
- 99.99% availability — interaction checker downtime means providers prescribe without safety nets
Scale Estimation
At 50,000 checks/minute peak, that is 833 checks/sec. Each check involves one candidate drug evaluated against an average of 8 active medications = 8 pairwise interaction lookups per check. Total lookup rate: 6,664 pairwise lookups/sec at peak. The drug knowledge graph contains: 30,000 drug entities (including generic names, brand names, and combination products mapped to RxNorm concept IDs), 500,000 known drug-drug interaction pairs with severity ratings, 100,000 drug-allergy cross-reactivity mappings, 50,000 drug-disease contraindications, and 5,000 pharmacogenomic interactions. The total knowledge graph fits in 2GB of memory, making it a candidate for in-memory serving. Daily interaction check volume: 20M checks generating 200M pairwise lookups. Alert suppression data (provider overrides) generates 2M records/day.
High-Level Architecture
The Drug Interaction Checker is designed as a low-latency, high-availability service that integrates with EHR systems via CDS Hooks (the HL7 Clinical Decision Support Hooks standard). When a provider selects a medication in the EHR's order entry screen, the EHR fires a CDS Hook request (order-select hook) to the Interaction Service. The service evaluates all relevant interactions and returns CDS Cards — structured alert objects that the EHR renders inline in the prescribing workflow.
The architecture consists of three layers: the Knowledge Graph Layer (stores and indexes pharmacological knowledge), the Inference Engine (evaluates interactions and ranks alerts), and the Integration Layer (CDS Hooks API, FHIR medication list retrieval, alert suppression tracking). The Knowledge Graph is built from multiple authoritative sources: FDB (First Databank) MedKnowledge, NLM DailyMed drug labels, DrugBank for pharmacogenomics, and RxNorm for drug concept normalization. A weekly ETL pipeline ingests updates from these sources, transforms them into a unified graph schema, and deploys the updated graph to the serving layer.
The graph is served from an in-memory data structure (optimized hash maps in a Java/Kotlin service) rather than an external graph database, ensuring sub-millisecond lookup latency. Each interaction check loads the patient's active medication list via a FHIR MedicationRequest query to the EHR, normalizes all drugs to RxNorm CUIs (Concept Unique Identifiers), and evaluates all pairwise and n-ary interactions. The results are filtered through the Alert Fatigue Management layer, which suppresses alerts the provider has previously overridden for this patient and applies institutional override policies. All data in transit is encrypted with TLS 1.3, and any cached patient medication data is encrypted at rest with AES-256.
Core Components
Pharmacological Knowledge Graph
The knowledge graph is the foundation of the interaction checker. It is modeled as a property graph with node types: Drug (RxNorm CUI, generic name, brand names, ingredient list, therapeutic class ATC code), Allergy (allergen substance, cross-reactivity group), Disease (ICD-10 code, SNOMED CT concept), Gene (gene symbol, associated enzymes), and Interaction (connecting two or more nodes with properties: severity, clinical_effect, mechanism, evidence_level, references). Drug-drug interactions are stored as edges between Drug nodes with severity levels: Contraindicated (absolute, should never be co-prescribed), Severe (use only if benefits outweigh risks, requires monitoring), Moderate (may require dose adjustment or monitoring), Minor (minimal clinical significance). The graph also encodes drug class hierarchies using ATC classification — enabling class-level interaction detection (e.g., all SSRIs interact with MAOIs, not just individual drugs). The weekly ETL pipeline uses Apache Airflow orchestrating: FDB MedKnowledge XML parsing → RxNorm concept normalization via the NLM RxNorm API → graph construction and validation → serialization to a binary format → deployment to serving nodes via a blue-green rollout.
Interaction Inference Engine
The Inference Engine evaluates interactions at multiple levels of specificity. Given a candidate drug and a patient's medication list, it: (1) normalizes the candidate drug to its RxNorm CUI and extracts ingredients (important for combination drugs like lisinopril/hydrochlorothiazide), (2) for each ingredient, looks up direct DDI edges in the knowledge graph against each active medication's ingredients — this catches interactions even when the interacting drugs are embedded in different combination products, (3) evaluates drug-class-level interactions by traversing the ATC hierarchy (e.g., candidate is an SSRI, patient takes an MAOI → class-level severe interaction), (4) checks the patient's allergy list against the drug's ingredient and class cross-reactivity mappings, (5) checks the patient's active diagnoses against drug-disease contraindications, (6) if pharmacogenomic data is available, checks drug-gene interactions. All found interactions are severity-ranked and deduplicated (if both a specific drug-drug and a class-level interaction are found, the more specific one takes precedence). The engine returns a ranked list of CDS Cards, each containing: severity, interacting drugs, clinical effect description, mechanism, recommended action, and literature references.
Alert Fatigue Management
Alert fatigue is the primary usability challenge in clinical decision support — studies show that providers override 90%+ of drug interaction alerts, many of which are clinically insignificant. The Alert Fatigue Manager implements multiple strategies: (1) severity filtering: institutions can configure minimum severity thresholds (e.g., suppress Minor alerts entirely), (2) provider-specific override memory: if a provider has overridden the same interaction for the same patient 3 times, the alert is suppressed with a subtle indicator instead of a modal interruption, (3) tiered presentation: Contraindicated interactions display as hard-stop alerts requiring a documented reason to override, Severe as interruptive alerts, Moderate as passive warnings in the sidebar, (4) contextual relevance scoring: interactions that are well-known and routinely managed (e.g., warfarin + acetaminophen at low doses) are weighted lower than rare, dangerous interactions. Override data is stored in PostgreSQL: overrides (override_id, provider_id, patient_id, drug_pair_rxcui, interaction_id, override_reason, timestamp). Analytics dashboards track override rates by severity and identify interactions that may need reclassification.
Database Design
The knowledge graph is stored in PostgreSQL for persistence and editorial workflows, but served from an in-memory data structure for runtime queries. PostgreSQL tables: drugs (rxcui PK, generic_name, brand_names TEXT[], atc_codes TEXT[], ingredients_rxcui INT[], drug_form), interactions (interaction_id, drug_a_rxcui, drug_b_rxcui, interaction_type DDI/ALLERGY/DISEASE/GENE, severity, clinical_effect, mechanism, evidence_level, source, last_reviewed, references JSONB), allergy_cross_reactivity (allergen_id, substance_rxcui, cross_reactive_class, cross_reactive_substances_rxcui INT[]), drug_disease_contraindications (id, drug_rxcui, disease_icd10, disease_snomed, severity, clinical_effect). A unique constraint on (drug_a_rxcui, drug_b_rxcui, interaction_type) prevents duplicate interaction entries.
The runtime serving layer loads the entire graph into memory at startup: a HashMap<RxCUI, Set
API Design
POST /cds-services/order-select— CDS Hooks endpoint invoked by the EHR when a provider selects a medication; body contains FHIR context (patient, proposed MedicationRequest, current medications); returns CDS Cards array with interaction alerts ranked by severityGET /v1/interactions?drug=rxcui:12345&against=rxcui:67890,rxcui:11111— Direct pairwise interaction lookup; returns all known interactions between the specified drugs with severity, mechanism, and clinical effectPOST /v1/interactions/batch— Batch interaction check for a full medication reconciliation; body contains patient's complete medication list; returns all pairwise interactions found across the listPOST /v1/overrides— Record a provider override; body contains provider_id, patient_id, interaction_id, override_reason; used for alert fatigue tracking and quality analytics
Scaling & Bottlenecks
The 200ms latency budget is allocated as: FHIR medication list retrieval from EHR (80ms budget, cached when possible), RxNorm normalization (20ms, in-memory lookup table), pairwise interaction evaluation (10ms for 8 medications = 28 pairs against in-memory graph), allergy/disease/gene checks (20ms), alert fatigue filtering (10ms, Redis-cached override history), response serialization (10ms), network overhead (50ms). The FHIR medication retrieval from the upstream EHR is the largest latency contributor and the least controllable. This is mitigated by: (1) caching the patient's active medication list in Redis with a 5-minute TTL, refreshed on every interaction check (so subsequent checks during the same prescribing session use cache), (2) the EHR can include the medication list in the CDS Hooks context (prefetch), eliminating the separate FHIR call entirely.
Availability is ensured by running the Interaction Service as a stateless, horizontally scaled deployment on Kubernetes with 10+ pods across 3 availability zones. The in-memory knowledge graph is loaded at pod startup (30-second initialization) from a pre-serialized binary in S3. Blue-green deployment ensures zero-downtime graph updates: new pods load the updated graph and are added to the load balancer only after passing a health check that validates graph integrity (expected node count, known interaction pair verification).
Key Trade-offs
- In-memory graph over graph database (Neo4j): Serving the knowledge graph from in-memory hash maps provides sub-millisecond lookup latency essential for the 200ms SLA, but requires all nodes to hold the full graph in memory (2GB per pod) and complicates graph updates — mitigated by blue-green deployment and the modest graph size
- CDS Hooks standard over proprietary EHR integration: CDS Hooks provides a standard integration pattern across Epic, Cerner, and other EHRs, reducing per-EHR integration effort, but limits the UX to the EHR's CDS Card rendering (no custom UI) — acceptable since the primary goal is decision-relevant alerts, not a standalone application
- Alert suppression to reduce fatigue over showing all interactions: Suppressing low-severity and previously-overridden alerts improves provider workflow and reduces override rates, but risks masking a clinically significant interaction that was previously overridden in a different context — mitigated by never suppressing Contraindicated alerts and periodic override audit reports
- Weekly knowledge graph updates over real-time FDA feed ingestion: Weekly batch updates from curated sources ensure pharmacist review before deployment, but delays incorporation of newly discovered interactions by up to 7 days — emergency updates (FDA safety communications) bypass the weekly cycle and deploy within 24 hours via a fast-track pipeline
GO DEEPER
Master this topic in our 12-week cohort
Our Advanced System Design cohort covers this and 11 other deep-dive topics with live sessions, assignments, and expert feedback.