In un sistema informativo ospedaliero le integrazioni point-to-point crescono con il quadrato dei sistemi connessi, e ogni nuovo dipartimentale costringe a ritoccare quelle già in piedi. Un integration engine come Mirth Connect serve a rompere questa crescita: i sistemi smettono di parlarsi a vicenda e scrivono o leggono da un punto centrale che ascolta, valida, trasforma, instrada e registra.
Contesto
Mirth Connect nasce nel 2006 in WebReach, poi Mirth Corporation, e oggi lo mantiene NextGen Healthcare con il nome NextGen Connect Integration Engine — anche se il marchio storico Mirth resta quello che usano tutti. Il codice è pubblico su GitHub (nextgenhealthcare/connect) e dalla serie 4.0, uscita a marzo 2021, è distribuito sotto Mozilla Public License 2.0 (prima era MPL 1.1). La stessa serie 4.0 ha aggiornato il motore JavaScript Rhino, ha introdotto un debugger per gli script di canale e ha allineato il supporto a Java 11.
L’engine fa una cosa sola: traduce fra dialetti. Due reparti dello stesso ospedale emettono entrambi HL7 v2.x, ma uno ordina i segmenti OBX a modo suo, un altro popola PID-18 dove il primo usa PID-3, un terzo si aspetta l’ACK con un timeout diverso. HL7 v2 è uno standard sulla carta, ma questo non rende i sistemi davvero interoperabili, e un livello di mediazione resta indispensabile.
Architettura a canali
L’unità di lavoro è il canale: una pipeline con una sorgente e una o più destinazioni.
Source connector → Source filter → Source transformer
│
┌───────────────────┼───────────────────┐
Destination 1 Destination 2 Destination N
(filter+transformer) (filter+transformer) (filter+transformer)
Il source connector riceve il messaggio grezzo. In sanità il caso più frequente è il listener TCP/MLLP (Minimal Lower Layer Protocol), che racchiude i messaggi HL7 v2 in una cornice di byte di controllo (0x0B in testa, 0x1C 0x0D in coda). Gli altri connettori coprono HTTP/HTTPS, lettura di directory, polling JDBC, JMS, SMTP/IMAP e DICOM C-STORE.
Quasi tutta la logica vive nel transformer. Mirth converte il messaggio in ingresso in un oggetto navigabile e ti lascia manipolarlo con JavaScript. Per HL7 v2 quell’oggetto è una rappresentazione E4X (ECMAScript for XML), una vecchia estensione del linguaggio che Rhino implementa: i segmenti diventano nodi XML a cui si accede con notazione diretta.
// msg = messaggio in ingresso (E4X), tmp = messaggio in uscita
var cognome = msg['PID']['PID.5']['PID.5.1'].toString();
if (msg['MSH']['MSH.9']['MSH.9.1'].toString() === 'ORU') {
tmp['OBR']['OBR.4']['OBR.4.2'] = mappaCodice(cognome);
}
E4X è stato deprecato negli ambienti JavaScript mainstream già da anni e non rientra in nessuna edizione ratificata di ECMAScript; sopravvive in Mirth perché Rhino lo implementa e perché navigare i segmenti HL7 v2 in questo modo dà un vantaggio sintattico concreto. È bene tenerlo a mente: il codice dei transformer non è JavaScript portabile.
Le destination ripropongono in uscita lo stesso repertorio di connettori, ciascuna con il proprio filtro e trasformatore. Una stessa pipeline può quindi fare fan-out: riceve un ORU^R01 di laboratorio e lo scrive in contemporanea nella cartella clinica via MLLP, in un data warehouse via JDBC e in un client REST.
I formati gestiti
Mirth tratta nativamente diversi formati clinici, con parser dedicati:
- HL7 v2.x — accesso ai segmenti via E4X, validazione verso profili, generazione automatica dell’
ACK. - HL7 CDA R2 / HL7 v3 — XML navigabile con XPath, usato per i documenti destinati al fascicolo sanitario.
- FHIR R4 (lo standard normativo HL7 pubblicato nel 2019) — payload JSON/XML; l’engine espone API Java per costruire client REST e gestire le risorse
Bundle. - X12, DICOM, XML/JSON/Delimited — formati amministrativi, di imaging e generici.
Questa varietà sposta a poco a poco l’uso dell’engine dal routing puro alla conversione fra standard: prendere HL7 v2 da un HIS legacy e produrre risorse FHIR Observation o DiagnosticReport per una piattaforma dati più recente.
Il punto critico
Il modello a canali è pulito finché il transformer resta una funzione pura sul messaggio. Cede quando lo si piega a ciò per cui non è pensato: chiamate sincrone a sistemi esterni dentro al transformer. Una Observation FHIR che, per essere costruita, deve fare una lookup HTTP verso un terminology server lega il throughput del canale alla latenza di quel server. Sotto un picco di laboratorio — migliaia di ORU in pochi minuti — il pool di thread del source connector si satura in attesa di I/O, la coda si gonfia, e il sintomo compare lontano dalla sua causa.
Il secondo punto fragile è la natura degli artefatti. Le configurazioni dei canali sono dati — XML serializzato con JavaScript annidato nei nodi — non file sorgente in un albero di progetto. Le si può esportare, versionare in Git e mettere in revisione, ma niente nello strumento lo impone: la via di minima resistenza è modificare il canale dall’Administrator e cliccare Deploy. Da lì nascono le derive operative più comuni: logica che esiste solo in produzione, nessuna storia delle modifiche oltre l’audit log, transformer che ormai nessuno sa più rideployare da zero.
Conseguenze operative
Trattare le configurazioni come codice — export, repository, code review, deploy da pipeline tramite la CLI mirthcli — separa un engine governabile da un punto cieco. Il message store, dove i messaggi transitano e si conservano, va dimensionato con il Message Pruner attivo per canale: un’istanza che processa milioni di messaggi al giorno senza politica di ritenzione riempie il database e degrada le query del Message Browser, che poi è lo strumento con cui fai diagnosi quando un flusso si rompe.
Per i picchi, la mediazione verso sistemi esterni lenti va portata fuori dal transformer sincrono: una destination dedicata con coda e retry assorbe la variabilità di latenza senza bloccare l’ingestione a monte.
Limiti
Il clustering attivo-attivo non fa parte della distribuzione open source: c’è solo nell’estensione commerciale, e un’alta affidabilità sul codice MPL va costruita a mano, con failover esterno e un database condiviso. Affidarsi a Rhino ed E4X significa che la competenza richiesta non è JavaScript generico ma un suo dialetto datato, con un bacino di sviluppatori ristretto. E un engine resta un punto di centralizzazione: riduce il grafo delle integrazioni, ma in cambio concentra un rischio operativo che va presidiato come tale.
Le alternative open source — Apache Camel con camel-hl7 e camel-fhir, WSO2 Enterprise Integrator, Apache NiFi per i dataflow — propongono modelli diversi: in genere più codice e più flessibilità, a fronte di meno strumenti verticali già pronti. La scelta dipende da quanto del problema è routing HL7 v2 classico e quanto è integrazione con sistemi recenti.
- https://github.com/nextgenhealthcare/connect
- https://github.com/nextgenhealthcare/connect/wiki/4.0.0---What’s-New
- https://www.hl7.org/implement/standards/product_brief.cfm?product_id=185 (HL7 v2)
- https://www.hl7.org/fhir/R4/
- https://www.hl7.org/implement/standards/product_brief.cfm?product_id=7 (CDA R2)
- https://www.mozilla.org/en-US/MPL/2.0/
- https://www.noze.it/insights/mirth-connect-integration-engine/
Immagine di copertina: Diagramma del segmento di intestazione MSH di un messaggio HL7 versione 2, con i campi separati dal carattere pipe — diagramma di Dipl.-Inform. Oliver S. Lazar, pubblico dominio — https://commons.wikimedia.org/wiki/File:HL7_msh.png