Capsule Informatiche di Lello De Luca
← Tutte le Capsule

Hexagonal Architecture: Ports & Adapters per servizi realmente sostituibili

Ports & Adapters mette il dominio al centro e rende infrastruttura e integrazioni implementazioni sostituibili di contratti espliciti.

L'idea centrale

La Hexagonal Architecture, spesso chiamata Ports & Adapters, separa la logica di business da ciò che cambia più spesso: database, framework web, broker, protocolli, integration engine e sistemi esterni. Il dominio non «chiama Mirth» e non «legge Mongo»: chiede a una porta di pubblicare un ordine o di recuperare un paziente.

Inbound e outbound ports

  • Inbound Port: espone un caso d'uso del sistema, per esempio AdmitPatient, PlaceOrder o MergePatient.

  • Inbound Adapter: REST controller, consumer RabbitMQ, job schedulato o comando CLI che invoca il caso d'uso.

  • Outbound Port: descrive una dipendenza necessaria al caso d'uso, per esempio PatientRepository, ImagingViewerPort o OrderIntegrationPort.

  • Outbound Adapter: PostgreSQL, FHIR client, HL7/MLLP, Mirth, VueMotion, DICOMweb o un adapter legacy.

La direzione delle dipendenze

Le dipendenze di compilazione puntano verso il dominio. Il core definisce le interfacce che gli servono; l'infrastruttura le implementa. In questo modo cambiare database o provider non impone di riscrivere le regole clinico-amministrative.

Esempio

Order Management può definire OrderPublisher.publish(OrderPlaced). In un'installazione l'adapter produce HL7 ORM via Mirth; in un'altra usa FHIR ServiceRequest; durante la migrazione un LegacyOrderAdapter può ancora chiamare il sistema precedente. Il dominio dell'ordine resta uguale.

Perché è diverso da una classica architettura a layer

In una struttura Controller → Service → Repository è facile che Service importi client esterni e framework. Nell'architettura esagonale il confine è esplicito e verificabile: il domain module non deve dipendere dagli adapter. Test architetturali possono impedire regressioni.

FHIR al confine

FHIR è molto utile come formato di interoperabilità, ma non deve necessariamente diventare il modello interno di ogni bounded context. Un FhirAdapter può tradurre tra il modello di dominio e Patient, Encounter, ServiceRequest o Observation, mantenendo il core indipendente dalla versione e dai profili richiesti dal singolo contesto di integrazione.

Quando non serve complicare

Per un CRUD molto semplice senza regole e senza probabili sostituzioni, una struttura più diretta può bastare. Ports & Adapters crea valore soprattutto quando il dominio è importante e le dipendenze esterne sono numerose o variabili.