Integrazioni interne ed esterne

Le integrazioni collegano l’applicazione generata da Lino ad altri moduli, servizi interni e sistemi di terze parti. La scelta tra chiamata sincrona, integrazione in-process, HTTP ed eventi deve considerare accoppiamento, latenza, affidabilità e proprietà dei dati.

L’obiettivo non è solo chiamare un altro sistema. È dichiarare un confine: chi fornisce il dato, chi lo consuma, quale contratto è pubblico, cosa accade in caso di errore e se l’operazione deve essere immediata o può essere elaborata in seguito.

Creare integrazioni

Le integrazioni rappresentano una comunicazione esplicita con un altro contesto, servizio interno o sistema esterno. Devono avere nome, contratto, autenticazione, timeout, strategia di errore e responsabilitĂ  chiara.

lino integration new --name <ServiceName>
lino integration list

Usare integration list per controllare le integrazioni esistenti prima di crearne un’altra. Evitare integrazioni generiche, come CommonIntegration, perché tendono ad accumulare responsabilità senza un limite chiaro.

Prima di creare un’integrazione, definire quale problema risolve: consultare un registro esterno, inviare un addebito, validare una sottoscrizione, sincronizzare un utente, consumare un modulo interno o pubblicare dati verso un altro sistema. Se l’integrazione non ha un’intenzione specifica, probabilmente non è ancora pronta per diventare un contratto generato.

  • Assegnare il nome in base al contesto integrato: preferire Billing, Identity, Catalog o Shipping a nomi generici.
  • Separare contratto e implementazione: dominio e applicazione non devono dipendere direttamente dai dettagli di trasporto.
  • Presumere errori esterni: ogni integrazione remota può diventare lenta, indisponibile, restituire un errore parziale o cambiare contratto.

Resources di integrazione

I resources rappresentano oggetti esposti o consumati da un’integrazione, come customers, invoices, tenants, subscriptions, users o documents.

lino integration resource new --service <ServiceName> --module <ModuleName> --entity <EntityName>
lino integration resource list --service <ServiceName> --module <ModuleName> --entity <EntityName>

Un resource di integrazione non deve necessariamente coincidere con l’entità di dominio. Spesso è un contratto di scambio dati, una vista esterna o una rappresentazione minima necessaria per la comunicazione tra contesti. Copiare l’intera entità nel contratto esterno di solito espone dettagli interni e aumenta il costo di evoluzione.

  • Modellare il resource con il vocabolario del sistema integrato.
  • Non esporre entitĂ  interne come contratto esterno.
  • Definire quali campi sono identificatori, filtri e dati restituiti.
  • Documentare paginazione, autenticazione e limiti di chiamata quando esistono.

Quando il resource rappresenta dati di un altro modulo, includere solo ciò che serve al consumatore. Lo stesso principio appare nelle shadow entities: il modulo consumatore mantiene una piccola copia locale, allineata al proprio Use Case, invece di dipendere dal modello completo del modulo produttore.

Operazioni di integrazione

Le operazioni descrivono le azioni disponibili su un resource di integrazione: creare, consultare, aggiornare, annullare, inviare, sincronizzare o validare.

lino integration operation new --service <ServiceName> --module <ModuleName> --entity <EntityName>
lino integration operation list --service <ServiceName> --module <ModuleName> --entity <EntityName>

Ogni operazione deve definire intenzione, input, output, metodo di chiamata, comportamento di errore e se può essere rieseguita in sicurezza.

  • Le query devono avere timeout e gestione dell’indisponibilitĂ .
  • I comandi remoti devono essere idempotenti quando possibile.
  • Gli errori esterni non devono interrompere la transazione principale se il business accetta elaborazione asincrona.
  • Usare log e correlation id per tracciare le chiamate tra sistemi.

L’idempotenza è particolarmente importante nelle operazioni di scrittura. Se un tentativo fallisce dopo che il sistema esterno ha eseguito l’azione, un nuovo tentativo può duplicare un addebito, creare un record ripetuto o inviare un messaggio due volte. Quando possibile, usare chiavi di idempotenza, identificatori esterni o regole di riesecuzione documentate.

Consumare integrazioni

Dopo aver modellato integrazione, resource e operazione, usare il consumo per collegare l’applicazione al contratto generato. L’obiettivo è fare in modo che il Use Case dipenda da un’astrazione nominata, con input e output prevedibili, invece di distribuire chiamate HTTP, costruzione di URL, headers, parsing della risposta e gestione degli errori in vari handlers.

lino integration consume

I Use Cases devono dipendere da astrazioni, non da dettagli HTTP sparsi nel codice. Questo facilita test, cambio di implementazione e gestione uniforme degli errori. Evita anche che ogni endpoint dell’applicazione reinventi timeout, autenticazione, correlation id, serializzazione e mapping degli errori in modo diverso.

  • Validare i dati prima di chiamare un servizio esterno.
  • Usare cancellation token e timeout.
  • Mappare le risposte esterne in errori interni prevedibili.
  • Non salvare payload esterno grezzo come regola di dominio senza trasformazione.

HTTP vs. in-process

L’integrazione in-process è semplice e rapida quando i moduli vengono eseguiti insieme. HTTP crea un contratto più esplicito tra processi, ma aggiunge latenza, errori di rete e necessità di resilienza.

L’integrazione in-process non deve significare accesso libero a tutto. Anche nello stesso runtime, il consumatore deve comunicare tramite contratto, non tramite un’entità interna, DbContext o repository di un altro modulo. Il costo di rete è minore, ma il rischio di accoppiamento continua a esistere.

ScenarioScelta comuneOsservazione
Moduli nello stesso monolite modulareIn-process o contratto internoPreservare il confine; evitare di condividere entitĂ  e persistenza.
Servizio separato con deployment proprioHTTP o messaggisticaGestire rete, autenticazione, versionamento e indisponibilitĂ .
Processo asincrono e tollerante al ritardoEvento di integrazioneProgettare consumatori idempotenti e tracciabili.

HTTP è piÚ adatto quando produttore e consumatore hanno runtime separati o quando si vuole rendere il confine operativamente esplicito. In-process è adatto quando la stessa applicazione ospita i contesti e la risposta deve essere immediata, purchÊ la dipendenza resti dichiarata tramite contratto.

Eventi vs. integrazioni sincrone

Usare un’integrazione sincrona quando la risposta è necessaria per concludere l’operazione corrente. Usare un evento quando il consumatore può reagire dopo e la consistenza eventuale è accettabile.

Questa scelta è una decisione di business prima che tecnica. Se un ordine può essere creato solo dopo che il servizio di pagamento conferma l’autorizzazione, la chiamata sincrona può essere parte essenziale del Use Case. Se la creazione dell’ordine deve solo attivare una notifica, un aggiornamento di proiezione o una sincronizzazione verso un altro modulo, un evento tende a essere più appropriato.

NecessitĂ ApproccioAttenzione tecnica
Risposta immediata obbligatoriaIntegrazione sincronaTimeout, fallback, errore prevedibile e contratto stabile.
Effetto successivo tollerante al ritardoEvento di integrazioneOutbox, idempotenza, retries e log di correlazione.
Lettura frequente di pochi dati esterniShadow Entity o proiezione localeSincronizzazione, aggiornamento eventuale e modello minimo.
Si è verificato un errore non gestito. Ricarica 🗙