Lavorare con gli eventi

Nei sistemi moderni, seguendo le best practice architetturali come DDD (Domain-Driven Design) e Clean Architecture, gli eventi sono meccanismi fondamentali per modellare i cambiamenti di stato e la comunicazione asincrona tra componenti o sistemi. Un evento rappresenta qualcosa che è già accaduto nel dominio dell’applicazione e che può interessare altre parti del sistema o servizi esterni.

Eventi di Dominio

Gli eventi di dominio rappresentano fatti importanti che si sono verificati nel contesto dell'applicazione. Vengono generati e consumati internamente nel sistema, permettendo la propagazione dei cambiamenti di stato in modo disaccoppiato, cioè senza che gli oggetti debbano conoscersi direttamente.

Quando utilizzare un Evento di Dominio?

  • Ogni volta che un'operazione genera un cambiamento significativo nel dominio e vi sia la necessità di notificare o attivare altre parti del sistema.
  • Per mantenere il modello di dominio coeso, consentendo a processi differenti di essere avviati senza creare dipendenze dirette tra moduli o servizi.

In Lino, creare un nuovo evento di dominio è semplice. È possibile eseguire:

lino event new

Il wizard CLI richiederà:

  • Servizio – Il servizio in cui l'evento sarà creato.
  • Modulo – Il modulo in cui l'evento sarà creato (solo nei servizi modulari).
  • Entità – L'entità a cui l'evento sarà creato/associato.
  • Tipo di evento – Evento di dominio o evento di integrazione.
  • Nome dell'evento – Nome utilizzato nel dominio, associato all'entità.

Esempio

Creando l'evento di dominio UserCreated associato all'entità User, il sistema crea automaticamente un evento con il nome UserCreatedDomainEvent. Questo nome rende chiaro a qualsiasi parte del sistema che consuma l'evento che l'azione di creazione dell'utente è già stata completata.

Struttura generata da Lino:

MyApp/
└── src/
    └── Services/
        └── MyService/
            └── Domain/
                ├── MyApp.MyService.Domain.csproj
                └── Aggregates/
                    └── Users/
                        ├── User.cs
                        ├── Errors/
                        ├── Events/
                        │   └── UserCreatedDomainEvent.cs
                        ├── Repositories/
                        └── Resources/

Gestori di Eventi di Dominio

Un Domain Event Handler è una classe responsabile di reagire a un Evento di Dominio, eseguendo azioni relative allo stato interno dell'applicazione, sempre all'interno dello stesso contesto transazionale.

L'obiettivo principale di questi gestori è mantenere il sistema coeso e disaccoppiato, permettendo l'applicazione di regole aggiuntive senza sovraccaricare la logica centrale dell'entità o dell'aggregato.

Ad esempio, dopo la creazione di un User, potrebbe essere necessario aggiornare le statistiche, generare un log interno o notificare un altro aggregato. Queste azioni hanno senso all'interno del dominio e possono avvenire in modo sincrono, garantendo una consistenza immediata.

Tuttavia, le operazioni che dipendono da risorse esterne — come l'invio di email o chiamate a API di terze parti — non dovrebbero essere eseguite direttamente dagli eventi di dominio, poiché ciò vincolerebbe la transazione a compiti lenti o instabili. In questi casi, il dominio può generare un evento di integrazione (registrato nell'Outbox), che sarà elaborato successivamente in modo asincrono e resiliente.

Caratteristiche principali:

  • Reagisce a un evento, ma non lo modifica mai.
  • Esegue solo operazioni in-process legate alla consistenza del dominio.
  • Garantisce che tutto venga eseguito all'interno della stessa transazione.

Per creare un nuovo gestore di eventi di dominio, basta eseguire il comando:

lino event-handler new

Il wizard del CLI richiederà:

  • Servizio – Il servizio in cui verrà creato il gestore di eventi.
  • Modulo – Il modulo in cui verrà creato il gestore di eventi (solo nei servizi modulari).
  • Entità – L'entità in cui verrà creato il gestore di eventi.
  • Tipo di evento – Evento di dominio o evento di integrazione.
  • Evento – L'evento che sarà consumato.
  • Nome del gestore di eventi – Nome utilizzato che sarà associato all'entità e all'evento di dominio.

Esempio

Creando il gestore di eventi di dominio UserCreated associato all'entità User e all'evento UserCreatedDomainEvent, il sistema crea automaticamente un gestore di eventi con il nome UserCreatedDomainEventHandler.

Struttura generata da Lino:

MyApp/
└── src/
    └── Services/
        └── MyService/
            └── Application/
                ├── MyApp.MyService.Application.csproj
                └── UseCases/
                    └── Users/
                        ├── Commands/
                        ├── EventHandlers/
                        │   └── Domain/
                        │       └── UserCreatedDomainEventHandler.cs
                        ├── Logging/
                        ├── Queries/
                        └── Resources/

Eventi di Integrazione

Gli Eventi di Integrazione sono messaggi che indicano che è accaduto qualcosa di importante e devono essere condivisi con sistemi esterni o altri microservizi.

Diversamente dagli eventi di dominio, qui l’obiettivo è la comunicazione tra sistemi e la sincronizzazione degli stati.

Quando creare un Evento di Integrazione:

  • Quando una modifica nel tuo sistema deve essere riflessa in un altro sistema.
  • Quando il tuo microservizio deve pubblicare modifiche affinché altri microservizi possano reagire.

Principali differenze tra Domain Events e Integration Events:

Aspetto Evento di Dominio Evento di Integrazione
Pubblico target Interno Esterno
Accoppiamento Basso (interno) Necessario (tra sistemi)
Tempo di elaborazione Immediato Può essere asincrono, con garanzie di consegna
Persistenza necessaria Non obbligatoria Sì (per affidabilità e resilienza)

In Lino, creare un nuovo evento di integrazione è semplice. Puoi eseguire:

lino event new

La procedura guidata del CLI chiederà:

  • Servizio – Il servizio in cui verrà creato l'evento.
  • Modulo – Il modulo in cui verrà creato l'evento (solo per servizi modulari).
  • Entità – L'entità in cui verrà creato / associato l'evento.
  • Tipo di evento – Evento di dominio o evento di integrazione.
  • Nome dell'evento – Nome utilizzato per l'evento di integrazione, associato all'entità.

Esempio

Creando l'evento di integrazione UserCreated associato all'entità User, il sistema crea automaticamente un evento con il nome UserCreatedIntegrationEvent. Questo nome rende chiaro a qualsiasi parte del sistema che consuma l'evento che l'azione di creazione dell'utente è già stata completata.

Struttura generata da Lino:

MyApp/
└── src/
    └── Services/
        └── MyService/
            └── IntegrationEvents/
                ├── MyApp.MyService.IntegrationEvents.csproj
                └── Users/
                    └── UserCreatedIntegrationEvent.cs

Gestori di Eventi di Integrazione

Un Integration Event Handler è una classe responsabile di consumare un Evento di Integrazione, solitamente pubblicato da un altro servizio o contesto, e poi eseguire azioni specifiche nel proprio dominio.

Questi handler ricevono eventi tramite meccanismi di messaggistica (come RabbitMQ, Kafka, Azure Service Bus, ecc.), di solito insieme al pattern Outbox, che garantisce una consegna affidabile e un'elaborazione asincrona.

Ad esempio, quando un evento UserCreated viene pubblicato da un servizio di Identità, il corrispondente handler in un altro contesto può reagire inviando un'email di benvenuto o chiamando API esterne. Queste operazioni possono essere più lente o soggette a errori, ma poiché vengono gestite al di fuori della transazione principale, non compromettono la coerenza interna dell'applicazione.

Caratteristiche principali:

  • Reagisce a eventi che rappresentano fatti di business rilevanti per altri contesti.
  • Esegue operazioni che possono essere lente o esterne (es.: invio di email, chiamate a API).
  • Elaborato in modo asincrono e resiliente, spesso con tentativi di ripetizione e monitoraggio.
  • Garantisce che i fallimenti esterni non influenzino la transazione originale del dominio.
  • Facilita l'integrazione tra bounded context e sistemi distribuiti.

Per creare un nuovo gestore di eventi di integrazione, basta eseguire il comando:

lino event-handler

Il wizard CLI richiederà:

  • Servizio – Il servizio in cui verrà creato il gestore di eventi.
  • Modulo – Il modulo in cui verrà creato il gestore di eventi (solo nei servizi modulari).
  • Entità – L'entità in cui verrà creato il gestore di eventi.
  • Tipo di evento – Evento di dominio o evento di integrazione.
  • Servizio – Il servizio in cui esiste l'evento da consumare.
  • Modulo – Il modulo in cui esiste l'evento da consumare (solo nei servizi modulari).
  • Entità – L'entità in cui esiste l'evento da consumare.
  • Evento – L'evento che verrà consumato.
  • Nome del gestore di eventi – Nome che sarà associato all'entità e all'evento di integrazione.

Esempio

Creando il gestore di eventi di integrazione SendEmailOnUserCreated associato all'entità User e all'evento UserCreatedIntegrationEvent, il sistema crea automaticamente un gestore di eventi con il nome SendEmailOnUserCreatedIntegrationEventHandler.

Struttura generata da Lino:

MyApp/
└── src/
    └── Services/
        └── MyService/
            └── Application/
                ├── MyApp.MyService.Application.csproj
                └── UseCases/
                    └── Users/
                        ├── Commands/
                        ├── EventHandlers/
                        │   └── Integration/
                        │       └── SendEmailOnUserCreatedIntegrationEventHandler.cs
                        ├── Logging/
                        ├── Queries/
                        └── Resources/
Si è verificato un errore non gestito. Ricarica 🗙