Laboratory Information System with Real-Time Westgard QC for an ISO 15189 Lab Network
Client Context
The accrediting pathologist had already flagged the audit-trail gaps as a major non-conformity in the previous surveillance — 11 months before we were brought in. The finding had been open the entire time. The lab network — four sites across Ho Chi Minh City, Hanoi, and Da Nang — was running a 9-year-old LIS that logged at the row level, not the field level. ISO 15189 §5.10 requires field-level traceability: who changed which field, from what value, to what value, and why. The old system couldn't produce that. The re-accreditation window was 8 months away. The second constraint was harder: Westgard QC rules were applied manually. A QC officer would run a control sample, record the result on paper, plot it on a Levey-Jennings chart at the end of shift, and decide whether the run was in control. The problem is that 'end of shift' can be 4–6 hours after the control was run. If the analyzer drifted out of control at 9am and the QC officer checked at 3pm, every patient result released in those 6 hours was suspect. We were told 8% of QC-failed results had been released before the failure was detected — a figure that required clinician callbacks, re-testing, and re-billing. We delivered this work in partnership with Aratech Vietnam, who own the clinical and regulatory expertise. We owned the engineering.
The Challenge
Business Challenge
Internal Quality Control was paper-based and retrospective: out-of-control runs detected at end of shift, 4–6 hours after the control was run. 8% of patient results released before QC failure detected — each requiring clinician callbacks, re-testing, and potential liability exposure. ISO 15189 surveillance audit had flagged the audit-trail granularity as a major non-conformity. Re-accreditation in 8 months meant we had a hard deadline, not a target.
Technical Challenge
A heterogeneous analyzer fleet: Roche Cobas 6000, Sysmex XN-1000, Abbott Architect c8000, and Mindray BS-2000 — each speaking a slightly different HL7 v2 ORU/ORM dialect. Two older analyzers communicated via RS-232 serial bridge only (no TCP/IP). HL7 v2 is a standard in name; in practice, the same message type from a Cobas and an Architect have different segment order, different field encoding, and different use of the OBX-6 (units) field. The existing LIS couldn't enforce role-based access cleanly, and the audit trail was at the record level — a change to one field in a patient result created a single log entry with no before/after values. The Westgard rule engine had to be accurate to the clinical definition: the rules are mathematically precise (1₂s: any control > 2SD; R₄s: consecutive controls with range > 4SD; 4₁s: four consecutive controls all > 1SD in the same direction) and the evaluation order matters — a 1₂s warning doesn't fail the run, but a 1₂s with a concurrent 2₂s does.
Signals Before We Started
Westgard rules applied manually at end of shift — 4–6h detection lag for out-of-control runs
8% of QC-failed patient results released before failure detected; requires clinician callbacks
ISO 15189 surveillance audit: major non-conformity on audit-trail granularity (11 months open)
New analyzer onboarding: 6–10 weeks due to HL7 dialect differences requiring bespoke hand-coding
Pathologists waiting 12+ minutes for a cross-lab QC summary (pulled manually from 4 separate LIS instances)
Re-accreditation window: 8 months — hard deadline, not advisory
Our Solution
Overview
A modular LIS with a real-time Westgard rules engine that evaluates every control sample within 1 second of the result arriving from the analyzer. Patient results are automatically held when QC fails; explicit pathologist override with mandatory free-text justification is required to release them. The four hard engineering problems: (1) HL7 v2 dialect normalization — we built a per-analyzer profile system that maps each analyzer's actual message structure to a canonical internal form, with per-field validation rules and a replayable raw message archive; (2) RS-232 bridge — two legacy analyzers communicate via serial; we built a TCP socket server on the lab PC that reads the serial stream, encapsulates in HL7 v2, and forwards to the interface engine; (3) cryptographically chained audit log — we use HMAC-SHA256 with a chain key derived from the previous log entry, making it computationally infeasible to tamper with historical audit records without detection; (4) hybrid deployment — all PHI stays on-prem at each lab; a read-only anonymized replica replicates to Azure SQL in Singapore for cross-lab pathologist dashboards. The re-accreditation timeline drove every sequencing decision.
Architecture
.NET 8 modular monolith for the LIS core — deployed on-prem at each lab as a Windows service on the lab's existing server hardware (no new server procurement in the budget). Why on-prem? PHI cannot leave the lab network under the Vietnamese healthcare data regulations applicable to this accreditation — a cloud-first architecture would have required a legal exemption that would have blown the 8-month deadline. Angular 17 web app served from the lab's intranet. SQL Server 2022 as the system of record (the lab's IT team was SQL Server-native; PostgreSQL would have added an operational learning curve they didn't have capacity for). A dedicated HL7 v2 interface engine with per-analyzer dialect adapters and a replayable raw message archive. RS-232 bridges for the two legacy analyzers (TCP socket server wrapping the serial stream). A read-only replication path to Azure SQL in Singapore for the cross-lab pathologist dashboard — only anonymized, non-PHI projections are replicated. Identity via Azure AD with on-prem ADFS fallback (the labs had intermittent internet; the fallback was exercised in production in week 14).
Approach
- 1
Discovery with QC officers, accrediting pathologist, and lab techs — before any code
- 2
Westgard engine built and validated against 18 months of historical QC data as the primary trust artifact
- 3
Per-analyzer HL7 dialect profile: map each analyzer's actual message structure, don't assume the standard
- 4
RS-232 bridge for legacy analyzers: TCP socket server wrapping the serial stream in HL7 v2
- 5
HMAC-SHA256 cryptographic audit chain: tamper-evident, field-level, with before/after values
- 6
Hold-and-release state machine: auto-hold on QC fail; pathologist override requires mandatory justification
- 7
Hybrid deployment: on-prem LIS core + Azure SQL read replica for cross-lab dashboard only
- 8
Parallel run with legacy LIS for one full QC cycle per lab before cutover
Platform Modules
The system was delivered as the following modules — each with its own owner, integration contract and rollout plan.
HL7 v2 Interface Engine
Per-analyzer dialect profiles that map each analyzer's actual message structure to a canonical internal form — segment order, OBX encoding, unit notation, and timestamp format all vary by analyzer model and firmware version. Raw messages are archived immutably before parsing for replayability. The profile system reduced new-analyzer onboarding from 6–10 weeks to ~4 days.
RS-232 Bridge
TCP socket server running on the lab PC connected to legacy analyzers via serial cable. Reads the RS-232 byte stream, frames HL7 v2 ORU messages using the analyzer's proprietary delimiters, and forwards to the interface engine over localhost TCP. Handles serial port reconnection and byte-stream framing errors without losing a result.
Westgard Rules Engine
Real-time evaluator for the six standard Westgard rules (1₂s warning, 1₃s reject, 2₂s reject, R₄s reject, 4₁s reject, 10x reject) plus lab-defined variants. Evaluation runs within 1 second of the result arriving. The critical implementation detail: R₄s and 4₁s are stateful — they require a sliding window of consecutive control values per analyte per analyzer; a naive per-sample evaluator produces false negatives. Rule set is version-controlled and signed off by the accrediting pathologist each quarter.
Hold & Release State Machine
On `qc.failed`, the state machine transitions affected patient results from PENDING to HELD. A pathologist must explicitly transition them to RELEASED with a mandatory free-text clinical justification. The justification is captured in the cryptographic audit chain. The state machine prevents a background process or timeout from auto-releasing held results — every release is a deliberate clinical decision.
HMAC-SHA256 Audit Chain
Every field-level change appends an audit record containing: actor ID, timestamp, entity type, entity ID, field name, before-value, after-value, free-text reason, and an HMAC-SHA256 computed over the concatenation of those fields plus the previous record's hash. Tampering with any historical record breaks the chain from that point forward — detectable in O(n) with a single pass. ISO 15189 auditors can verify chain integrity from the evidence-export screen.
Cross-Lab Pathologist Dashboard
Angular 17 app on a read-only replica in Azure SQL (Singapore region). Shows cross-lab Levey-Jennings for each analyte, QC exception queue with hold-and-release status, and signed clinical sign-off for result batches. PHI fields are anonymized during replication — only analyte results, QC status, and lab metadata are replicated. The pathologist sees trends across all 4 labs without PHI leaving the local network.
Data Flow
An analyzer sends a result message (HL7 ORU^R01) to the on-prem interface engine — either via TCP/IP or via the RS-232 bridge. The interface engine validates the message against the per-analyzer dialect profile, writes the raw payload to the immutable message archive, and forwards a canonicalized result to the LIS Core. The LIS Core writes the result to SQL Server and immediately evaluates whether it is a QC sample. If it is a QC sample, the Westgard Engine evaluates the active rule set using a sliding window of the last 10 control values for that analyte on that analyzer. If any reject rule fires, the engine emits a `qc.failed` event. The Hold & Release state machine transitions all pending patient results for that analyzer/analyte to HELD and sends an alert to the on-duty QC officer and pathologist. Every state transition is appended to the HMAC-SHA256 audit chain in the same SQL Server transaction. Once per day, a read-only projection job extracts anonymized QC metrics (no patient identifiers) and replicates them to Azure SQL in Singapore, where the cross-lab pathologist dashboard reads them.
Integrations
Roche Cobas 6000, Abbott Architect c8000, Sysmex XN-1000, Mindray BS-2000 (HL7 v2 ORU/ORM)
Two legacy analyzers via RS-232 serial bridge (TCP socket server + HL7 v2 framing)
Hospital Information System (HIS) via HL7 v2 ORM/ORU for test orders and result delivery
ADFS on-prem identity provider with Azure AD hybrid join for fallback during internet outages
Billing module via CSV over secure SMB (same integration pattern as legacy LIS)
Delivery Timeline
Phased delivery — each phase had explicit goals, measurable outcomes and a checkpoint before progression.
Phase 1 — Discovery & Westgard baseline
Week 1–4Goals- ·Inventory all analyzers, HL7 dialects, and audit-trail gaps at all 4 labs
- ·Capture the exact Westgard rules in use per test family and analyte (1₂s, 1₃s, 2₂s, R₄s, 4₁s, 10x and lab-specific variants)
- ·Validate the rule engine implementation against 18 months of historical QC data with pathologist sign-off
Outcomes- ✓Engine reproduced 99.6% of historical QC pass/fail decisions; remaining 0.4% were data-entry errors in the legacy LIS (confirmed by pathologist)
- ✓Critical finding: R₄s rule requires consecutive samples; the legacy LIS was evaluating per-sample only — this was a clinical error, not a software gap
- ✓Pathologist signed off on the rule set in week 4 — this sign-off was the gate to phase 2
Phase 2 — LIS core & audit trail
Week 4–12Goals- ·Patient, Sample, Test, Result, QC, User, Audit domain model in code
- ·HMAC-SHA256 cryptographically chained audit log at the field level
- ·Hold-and-release state machine: auto-hold on QC fail; explicit pathologist release with mandatory justification
Outcomes- ✓Audit log: every field change records actor, timestamp, before-value, after-value, and cryptographic chain entry
- ✓First internal audit on the new LIS: zero findings on traceability — the open non-conformity was closeable
- ✓Hold-and-release state machine tested against 14 simulated out-of-control scenarios; all behaved correctly
Phase 3 — Analyzer integration
Week 10–22Goals- ·Per-analyzer HL7 v2 dialect profile for Cobas 6000, Architect c8000, Sysmex XN-1000, Mindray BS-2000
- ·RS-232 bridge for two legacy analyzers (TCP socket server wrapping serial stream)
- ·Bi-directional ORM (test orders) and ORU (results) flow; replayable raw message archive
Outcomes- ✓Full fleet connected at lab 1 by week 18, lab 2 by week 22
- ✓Hardest dialect issue: Cobas OBX-6 units field uses UCUM notation with trailing whitespace; the parser was stripping the whitespace and producing unit-mismatch warnings — resolved with a profile-specific post-processing step
- ✓Analyzer onboarding time: 6–10 weeks → ~4 days using the dialect profile framework
Phase 4 — Real-time Westgard engine
Week 16–24Goals- ·Evaluate every incoming QC sample against the active rule set in < 1 second
- ·Auto-hold patient results from affected analyzer/analyte on QC fail, with full audit
- ·Live Levey-Jennings visualization per analyzer per analyte in the lab tech view
Outcomes- ✓QC-fail detection lag: 4–6h → < 60 seconds
- ✓Patient results released before detection: 8% → 0.3% (residual cases are all documented manual overrides with mandatory justification)
- ✓R₄s rule correctly evaluates consecutive-sample pairs; the legacy error corrected
Phase 5 — Parallel run & ISO re-accreditation
Week 22–32Goals- ·Run new and legacy LIS in parallel for one full QC cycle per lab (minimum 4 weeks per lab)
- ·Train all QC officers, lab techs, and pathologists; certify on both audit and override procedures
- ·Support ISO 15189 surveillance audit with evidence packs from the new system
Outcomes- ✓All 4 labs cut over by week 30 (2 months before the accreditation deadline)
- ✓ISO 15189 surveillance audit completed; previous audit-trail non-conformity closed with zero new findings
- ✓Pathologist QC summary wait time: 12 minutes → < 5 seconds (cross-lab dashboard on Azure SQL replica)
Technology Stack
The Results
Measurable impact delivered within 7 months (Phase 1–5).
Security & Compliance
- ✓ISO 15189 §5.10 audit trail: field-level, HMAC-SHA256 cryptographic chain, tamper-detectable
- ✓All PHI stays on-prem at each lab; only anonymized QC metrics replicate to Azure SQL Singapore
- ✓Role-based access by lab role: technician, QC officer, pathologist, biomedical engineer, admin
- ✓Pathologist override on QC-fail requires mandatory free-text clinical justification in the audit chain
- ✓Encrypted SQL Server backups with 30-day point-in-time restore; quarterly restore drills
- ✓ADFS on-prem identity fallback: system remains operational during internet outages
- ✓Annual penetration test against the pathologist dashboard (internet-facing Azure SQL endpoint)
Delivery & Operations
- ✓GitHub Actions CI: Westgard rule engine reproducibility test (18 months of historical QC as golden files); HL7 dialect parser tests per analyzer model; hold-and-release state machine tests against 14 scenario scripts
- ✓On-prem deployments via versioned MSI packages with SQL migration scripts and rollback procedures
- ✓Per-lab staging environment refreshed from anonymized production data (anonymization pipeline built in phase 2, week 8)
- ✓Vireon + Aratech joint on-call: clinical questions and regulatory queries → Aratech; platform incidents → Vireon
- ✓Quarterly Westgard rule-set review with the accrediting pathologist; any rule change requires a re-run of the historical reproducibility test before deployment
Key Learnings
Reproducing 18 months of historical QC decisions was the single artifact that earned pathologist trust. Without a demonstrated 99.6% reproduction rate, no clinician would accept automated hold-and-release on patient results. This artifact should be the first thing built in any clinical decision-support system, not a validation afterthought.
ISO 15189 audit trails must be field-level and cryptographically chained — not row-level triggers. Auditors want to see exactly which field changed, by whom, from what value, to what value, and why. Anything less is a finding. The HMAC chain also makes the audit evidence legally defensible: it demonstrates the trail hasn't been altered after the fact.
HL7 v2 is not a standard — it's a framework for building incompatible implementations. The same OBX segment means different things on a Cobas vs an Architect vs a Sysmex. Build per-analyzer dialect profiles from day one; assume nothing about field encoding, segment order, or unit notation.
Hybrid deployment (on-prem clinical core + cloud read replica) is the realistic answer for regulated clinical software in this region. Pathologists need cross-lab views; regulators require PHI to stay on local infrastructure. An anonymized read-only replica resolves that tension without requiring a regulatory exemption.
Partnering with Aratech for the clinical and regulatory domain was decisive. Engineering velocity is necessary but not sufficient when the buyer is a pathologist, the product is a clinical system, and the auditor is the accreditation body. Domain expertise from a credentialed partner is not optional.
What We'd Do Differently
Every project teaches us something we hadn't anticipated. Here's what we'd change if we were starting again.
We built a fully configurable Westgard rule engine supporting arbitrary user-defined rules from day one. In production, the labs used 7 standard rules and defined zero custom ones. The configurability framework took 4 additional weeks to build. We should have shipped the 7 standard rules hardcoded first and treated configurability as an explicit phase 2. The principle: don't build extensibility for use cases that don't exist yet in a regulated environment with a hard deadline.
The per-lab staging environments refreshed from anonymized production data were essential for realistic testing, but the anonymization pipeline took 2 weeks to build and wasn't on the original plan. We absorbed the time. In regulated software, 'same as prod, just smaller' is never sufficient — you need genuine anonymization that passes a data-protection review, and that takes time you must plan for explicitly.
Similar Projects
Other high-stakes systems we've shipped.
Let's Discuss Your Project
Schedule a free consultation to explore how we can help you achieve your goals.