Un'integrazione smette di essere un dettaglio tecnico quando i suoi campi, codici di errore e regole di accesso compaiono in controller, servizi applicativi, modelli e processi di business. A quel punto, cambiare fornitore, aggiornare un'API o gestire un incidente esterno richiede di modificare parti dell'applicazione che non dovrebbero conoscere quel sistema.
Isolare le integrazioni esterne in PHP consiste nello stabilire un confine chiaro: il dominio esprime ciò di cui ha bisogno nel proprio linguaggio e uno strato di infrastruttura traduce tale necessità nel protocollo, nel formato e nel comportamento concreti del fornitore. Non si tratta di nascondere un'API dietro una classe con un altro nome, ma di impedire che le sue decisioni condizionino l'intera applicazione.
Quando un'integrazione sta già contaminando l'applicazione

L'accoppiamento tende a crescere in modo incrementale. Un team consuma un'API da un controller per risolvere una consegna urgente; poi un altro flusso riutilizza lo stesso client; infine, gli array di risposta e le eccezioni dell'SDK diventano dipendenze implicite del business.
- I casi d'uso ricevono o restituiscono array con nomi di campi del fornitore.
- La logica di business confronta codici esterni, come
ERR_42oPENDING_REVIEW. - Controller, comandi e job in coda costruiscono direttamente richieste HTTP.
- Le eccezioni dell'SDK esterno vengono intercettate al di fuori dell'infrastruttura.
- Una modifica a credenziali, endpoint o versioni obbliga a modificare vari moduli.
- I test del dominio necessitano di connessioni reali, token o risposte simulate del fornitore.
Questi segnali non significano che debba iniziare una riscrittura completa. Indicano però che è opportuno dare priorità all'integrazione in base al suo rischio: criticità per il business, frequenza dei cambiamenti del fornitore, volume dei consumatori interni, sensibilità dei dati e difficoltà di recupero da un guasto.
Definire contratti a partire dal linguaggio di business
Il contratto interno, chiamato anche porta, deve descrivere una capacità di cui l'applicazione ha bisogno, non una riproduzione del catalogo di operazioni di un'API esterna. Per esempio, un'applicazione di prenotazioni può aver bisogno di “richiedere una prenotazione”, “consultarne lo stato” e “annullarla”. Non ha bisogno che il dominio sappia che un fornitore usa XML, OAuth, un identificatore numerico o una particolare convenzione di retry.
Una porta può essere espressa come un'interfaccia PHP:
interface ReservationGateway
{
public function request(ReservationRequest $request): ReservationResult;
public function status(ReservationReference $reference): ReservationStatus;
public function cancel(ReservationReference $reference): void;
}I tipi del contratto devono appartenere al linguaggio interno. ReservationRequest contiene i dati necessari alla decisione di business; non dovrebbe includere campi di autenticazione, header HTTP né nomi ereditati dal fornitore. Allo stesso modo, un riferimento interno può incapsulare l'identità esterna senza renderla predominante in tutti i casi d'uso.
I componenti di un confine di integrazione
Porta, adattatore e DTO interno
La porta è l'interfaccia consumata dall'applicazione. L'adattatore è l'implementazione che comunica con il sistema esterno. Tra i due, i DTO interni trasferiscono dati con una struttura stabile per l'applicazione.
L'adattatore traduce in entrambe le direzioni: converte il DTO interno in una richiesta specifica e normalizza la risposta in un risultato che il dominio possa interpretare. Se il fornitore cambia guest_count in travellers, la modifica deve rimanere circoscritta a quell'adattatore.
Configurazione, credenziali e trasporto
Endpoint, token, timeout, certificati e politiche di retry sono aspetti dell'infrastruttura. Devono essere iniettati tramite configurazione e mantenuti fuori da entità e servizi di dominio. È inoltre opportuno separare il client HTTP o l'SDK dall'adattatore: ciò facilita la sostituzione delle librerie, la registrazione della telemetria e il test del mapping senza dipendere dal trasporto reale.
Traduzione degli errori e stati incerti
Non tutti i guasti ricevono lo stesso trattamento. Un errore di validazione rifiutato dal fornitore può essere recuperabile per l'utente; un errore di autenticazione richiede un intervento operativo; un timeout può lasciare uno stato incerto perché il fornitore potrebbe aver elaborato la richiesta.
Il contratto interno deve rappresentare queste differenze senza far filtrare eccezioni estranee. Per esempio, l'adattatore può trasformare una risposta di validazione in ReservationRejected, un problema temporaneo in TemporaryUnavailable e un timeout dopo l'invio della richiesta in UnknownSubmissionState. Quest'ultimo non deve essere trattato come un semplice errore: può richiedere una verifica successiva tramite una chiave di idempotenza o una riconciliazione operativa.
Tradurre gli errori non significa eliminare i dettagli. Registrare in modo sicuro l'identificatore di correlazione, la causa tecnica e la risposta rilevante, evitando di esporre segreti o dati sensibili all'utente.
Esempio di incapsulamento di un servizio di prenotazioni
Supponiamo che un fornitore richieda una richiesta JSON con date in un formato specifico, un proprio codice hotel e un header di autorizzazione. Il caso d'uso interno non dovrebbe costruire tale richiesta. Riceve una richiesta di prenotazione, applica le proprie regole e chiama ReservationGateway.
L'adattatore ExternalReservationAdapter svolge i compiti specifici:
- Converte l'identificatore interno della struttura ricettiva nel codice riconosciuto dal fornitore.
- Formatta date, ospiti e preferenze secondo il contratto esterno.
- Aggiunge credenziali e una chiave di idempotenza.
- Interpreta codici HTTP, corpi di errore e stati specifici.
- Restituisce un riferimento e uno stato interni.
L'applicazione conserva la regola su quando una prenotazione è accettabile; l'adattatore conserva la regola su come richiederla a quel fornitore. Se viene aggiunto un secondo fornitore, può essere implementata la stessa porta, purché la capacità di business sia equivalente. Se non lo è, forzare un'interfaccia comune può nascondere differenze importanti e creare decisioni ambigue.
Come estrarre un'integrazione già accoppiata
La migrazione sicura non richiede di fermare l'evoluzione del prodotto. Iniziate con un inventario: individuate chiamate dirette, classi dell'SDK, formati esterni, eccezioni filtrate e consumatori. Identificate prima i percorsi critici o quelli che cambiano con maggiore frequenza.
- Introducete una facciata: create la porta e un adattatore iniziale che possa riutilizzare temporaneamente parte del client esistente.
- Fate migrare i consumatori per flusso: sostituite le chiamate dirette di un caso d'uso alla volta. Evitate di mantenere due interpretazioni diverse dello stesso errore.
- Centralizzate il mapping: rimuovete conversioni di campi e codici esterni da controller, servizi e template.
- Aggiungete osservabilità: registrate latenza, risultati, errori normalizzati e correlazione tra richiesta interna e chiamata esterna.
- Rimuovete l'accesso diretto: quando non rimangono consumatori, limitate o eliminate il client esposto per evitare regressioni.
Durante la transizione, la facciata non deve diventare un contenitore generico di metodi dell'SDK. Il suo scopo è definire un confine utile e stabile, non spostare l'accoppiamento in un'altra cartella.
Test e criteri per validare l'isolamento
I test del dominio devono usare test double della porta. In questo modo verificano decisioni di business senza rete, credenziali né comportamenti accidentali del fornitore. I test dell'adattatore, invece, devono verificare il mapping di richieste, risposte ed errori rispetto a un ambiente controllato, un server simulato o contratti documentati dal sistema esterno.
Il risultato è verificabile se sono soddisfatti questi criteri:
- Una modifica di formato, endpoint o SDK è concentrata nell'adattatore e nella sua configurazione.
- I casi d'uso dipendono da contratti interni, non da client HTTP né da tipi esterni.
- Le eccezioni e i codici del fornitore non attraversano il confine.
- Gli stati incerti hanno un trattamento esplicito, inclusa l'idempotenza o una verifica successiva quando necessaria.
- I test di business vengono eseguiti con test double e quelli di integrazione validano la traduzione reale.
Errori comuni prima di aggiungere un altro fornitore

L'astrazione prematura è un rischio: non create una gerarchia complessa per un'unica integrazione stabile senza reali necessità di sostituzione. Anche l'estremo opposto fallisce: replicare l'intera API esterna in un'interfaccia interna fa sì che il dominio erediti la sua complessità.
Prima di integrare, confermate quale capacità serve al business, chi possiede ciascun dato, quali errori sono azionabili, come si evita la duplicazione delle operazioni e cosa accadrà se non arriva una risposta. Definite la porta in base a queste decisioni, implementate l'adattatore come traduttore e mantenete le peculiarità esterne al margine. Questa disciplina consente di isolare le integrazioni esterne in PHP senza trasformare ogni cambiamento di fornitore in una modifica trasversale dell'applicazione.



