Working with events

In modern systems, following architectural best practices like DDD (Domain-Driven Design) and Clean Architecture, events are fundamental mechanisms for modeling state changes and asynchronous communication between components or systems. An event represents something that has already happened in the application domain and that may be of interest to other parts of the system or external services.

Domain Events

Domain events represent important facts that occurred within the context of the application. They are generated and consumed internally in the system, allowing state changes to propagate in a decoupled manner, meaning that objects do not need to know each other directly.

When to use a Domain Event?

  • Whenever an operation generates a significant change in the domain and there is a need to notify or trigger other parts of the system.
  • To keep the domain model cohesive, enabling different processes to be triggered without creating direct dependencies between modules or services.

In Lino, creating a new domain event is simple. You can execute:

lino event new

The CLI wizard will request:

  • Service – The service in which the event will be created.
  • Module – The module in which the event will be created (only in modular services).
  • Entity – The entity to which the event will be created/associated.
  • Event type – Domain event or integration event.
  • Event name – Name used in the domain, associated with the entity.

Example

Creating the domain event UserCreated associated with the User entity, the system automatically creates an event named UserCreatedDomainEvent. This name makes it clear to any part of the system consuming the event that the user creation action has already been completed.

Structure generated by Lino:

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

Domain Event Handlers

A Domain Event Handler is a class responsible for reacting to a Domain Event, executing actions related to the internal state of the application, always within the same transactional context.

The main purpose of these handlers is to keep the system cohesive and decoupled, allowing additional rules to be applied without overloading the core logic of the entity or aggregate.

For example, after creating a User, it may be necessary to update statistics, generate an internal log, or notify another aggregate. These actions make sense within the domain and can occur synchronously, ensuring immediate consistency.

However, operations that depend on external resources — such as sending emails or calling third-party APIs — should not be executed directly from domain events, as this would tie the transaction to slow or unstable tasks. In these cases, the domain can generate an integration event (recorded in the Outbox), which will be processed later in an asynchronous and resilient manner.

Main characteristics:

  • Reacts to an event but never modifies it.
  • Executes only in-process operations related to domain consistency.
  • Ensures that everything runs within the same transaction.

To create a new domain event handler, simply run the command:

lino event-handler new

The CLI wizard will request:

  • Service – The service in which the event handler will be created.
  • Module – The module in which the event handler will be created (only in modular services).
  • Entity – The entity in which the event handler will be created.
  • Event type – Domain event or integration event.
  • Event – The event that will be consumed.
  • Event handler name – The name used that will be associated with the entity and domain event.

Example

Creating the domain event handler UserCreated associated with the entity User and the event UserCreatedDomainEvent, the system automatically creates an event handler named UserCreatedDomainEventHandler.

Structure generated by Lino:

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

Integration Events

Integration Events are messages that indicate something important has happened and need to be shared with external systems or other microservices.

Unlike domain events, the goal here is communication between systems and state synchronization.

When to create an Integration Event:

  • When a change in your system needs to be reflected in another system.
  • When your microservice needs to publish changes so that other microservices can react.

Main differences between Domain Events and Integration Events:

Aspect Domain Event Integration Event
Target Audience Internal External
Coupling Low (internal) Necessary (between systems)
Processing Time Immediate Can be asynchronous, with delivery guarantees
Persistence Required Not mandatory Yes (for reliability and resilience)

In Lino, creating a new integration event is simple. You can run:

lino event new

The CLI wizard will ask for:

  • Service – The service in which the event will be created.
  • Module – The module in which the event will be created (only for modular services).
  • Entity – The entity in which the event will be created / associated.
  • Event Type – Domain event or integration event.
  • Event Name – Name used for the integration event, associated with the entity.

Example

Creating the integration event UserCreated associated with the entity User, the system automatically creates an event named UserCreatedIntegrationEvent. This name makes it clear to any part of the system consuming the event that the user creation action has already been completed.

Structure generated by Lino:

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

Integration Event Handlers

An Integration Event Handler is a class responsible for consuming an Integration Event, typically published by another service or context, and then executing specific actions within its own domain.

These handlers receive events through messaging mechanisms (such as RabbitMQ, Kafka, Azure Service Bus, etc.), usually together with the Outbox pattern, which ensures reliable delivery and asynchronous processing.

For example, when a UserCreated event is published by an Identity service, the corresponding handler in another context can react by sending a welcome email or calling external APIs. These operations may be slower or prone to failure, but since they are handled outside the main transaction, they do not compromise the internal consistency of the application.

Main features:

  • Reacts to events that represent business facts relevant to other contexts.
  • Performs operations that may be slow or external (e.g., sending emails, calling APIs).
  • Processed asynchronously and resiliently, often with retries and monitoring.
  • Ensures that external failures do not affect the original domain transaction.
  • Facilitates integration between bounded contexts and distributed systems.

To create a new integration event handler, simply run the command:

lino event-handler

The CLI wizard will request:

  • Service – The service in which the event handler will be created.
  • Module – The module in which the event handler will be created (only in modular services).
  • Entity – The entity in which the event handler will be created.
  • Event type – Domain event or integration event.
  • Service – The service where the event to be consumed exists.
  • Module – The module where the event to be consumed exists (only in modular services).
  • Entity – The entity where the event to be consumed exists.
  • Event – The event that will be consumed.
  • Event handler name – Name to be associated with the entity and integration event.

Example

Creating the integration event handler SendEmailOnUserCreated associated with the User entity and UserCreatedIntegrationEvent event, the system automatically creates an event handler named SendEmailOnUserCreatedIntegrationEventHandler.

Structure generated by Lino:

MyApp/
└── src/
    └── Services/
        └── MyService/
            └── Application/
                ├── MyApp.MyService.Application.csproj
                └── UseCases/
                    └── Users/
                        ├── Commands/
                        ├── EventHandlers/
                        │   └── Integration/
                        │       └── SendEmailOnUserCreatedIntegrationEventHandler.cs
                        ├── Logging/
                        ├── Queries/
                        └── Resources/
An unhandled error has occurred. Reload 🗙