Skip to main content
The architecture supports two types of events: Domain Events for internal bounded context communication, and Integration Events for cross-service communication.

Event Types

Domain Events

Domain events are internal to the bounded context and published via ICommandQueryBus using MediatR.

DomainEvent Base Class

namespace Core.Application
{
    public class DomainEvent : IRequestNotification
    {
        public DateTime EventDateUtc { get; private set; }

        public DomainEvent()
        {
            EventDateUtc = DateTime.UtcNow;
        }

        public DomainEvent(DateTime createDateUtc)
        {
            EventDateUtc = createDateUtc;
        }
    }
}
Source: Core.Application.ComandQueryBus/Notifications/DomainEvent.cs:3 Properties:
  • EventDateUtc - Timestamp when the event occurred (UTC)

IRequestNotification

Marker interface for domain events (extends MediatR’s INotification).
using MediatR;

namespace Core.Application
{
    public interface IRequestNotification : INotification
    {
    }
}
Source: Core.Application.ComandQueryBus/Notifications/IRequestNotification.cs:5

Integration Events

Integration events are used for communication between bounded contexts or microservices, typically via message brokers.

IntegrationEvent Base Class

using Newtonsoft.Json;

namespace Core.Application
{
    public abstract class IntegrationEvent
    {
        [JsonProperty]
        public Guid Id { get; private set; }

        [JsonProperty]
        public DateTime CreateDateUtc { get; private set; }

        [JsonProperty]
        public string EventType { get; private set; }

        [JsonProperty]
        public string Subject { get; private set; }

        [JsonProperty]
        public object Data { get; private set; }

        [JsonConstructor]
        protected IntegrationEvent()
        {
        }

        [JsonConstructor]
        protected IntegrationEvent(string eventType, string subject, object data)
        {
            Id = Guid.NewGuid();
            CreateDateUtc = DateTime.UtcNow.ToLocalTime();
            EventType = eventType;
            Subject = subject;
            Data = data;
        }

        [JsonConstructor]
        protected IntegrationEvent(Guid id, DateTime date, string eventType, string subject, object data)
        {
            Id = id;
            CreateDateUtc = date;
            EventType = eventType;
            Subject = subject;
            Data = data;
        }
    }
}
Source: Core.Application.EventBus/Events/IntegrationEvent.cs:5 Properties:
  • Id - Unique identifier for the event
  • CreateDateUtc - When the event was created
  • EventType - Type/category of the event
  • Subject - Subject or topic of the event
  • Data - Event payload (any serializable object)

Domain Event Example

DummyEntityCreated

Domain event published when a new entity is created.
using Core.Application;
using static Domain.Enums.Enums;

namespace Application.DomainEvents
{
    /// <summary>
    /// Ejemplo de un evento de dominio para la entidad Dummy.
    /// Todo evento de dominio debe heredar de <see cref="DomainEvent"/>
    /// </summary>
    internal sealed class DummyEntityCreated : DomainEvent
    {
        //Aqui se definen las propiedades compartidas en el evento
        public string Id { get; set; }
        public string DummyPropertyOne { get; set; }
        public DummyValues DummyPropertyTwo { get; set; }
    }
}
Source: Application/DomainEvents/DummyEntityCreated.cs:10

Publishing Domain Events

Domain events are published via ICommandQueryBus in command handlers:
public async Task<string> Handle(CreateDummyEntityCommand request, CancellationToken cancellationToken)
{
    Domain.Entities.DummyEntity entity = new(request.dummyPropertyOne, request.dummyPropertyTwo);

    if (!entity.IsValid) throw new InvalidEntityDataException(entity.GetErrors());

    if (_dummyEntityApplicationService.DummyEntityExist(entity.Id)) 
        throw new EntityDoesExistException();

    try
    {
        object createdId = await _context.AddAsync(entity);

        // Publish domain event
        await _domainBus.Publish(entity.To<DummyEntityCreated>(), cancellationToken);

        return createdId.ToString();
    }
    catch (Exception ex)
    {
        throw new BussinessException(ApplicationConstants.PROCESS_EXECUTION_EXCEPTION, ex.InnerException);
    }
}
Source: Application/UseCases/DummyEntity/Commands/CreateDummyEntity/CreateDummyEntityHandler.cs:34

Integration Event Example

DummyEntityCreatedIntegrationEvent

Integration event for cross-service communication.
using Core.Application;

namespace Application
{
    public class DummyEntityCreatedIntegrationEvent : IntegrationEvent
    {
        public DummyEntityCreatedIntegrationEvent()
        {
        }

        public DummyEntityCreatedIntegrationEvent(string eventType, string subject, object data) 
            : base(eventType, subject, data)
        {
        }

        public DummyEntityCreatedIntegrationEvent(Guid id, DateTime date, string eventType, string subject, object data) 
            : base(id, date, eventType, subject, data)
        {
        }
    }
}
Source: Application/Integrations/Events/DummyEntityCreatedIntegrationEvent.cs:5

Event Handlers

Integration Event Handler Interface

namespace Core.Application
{
    public interface IIntegrationEventHandler
    {
    }

    public interface IIntegrationEventHandler<in TEvent> : IIntegrationEventHandler 
        where TEvent : IntegrationEvent
    {
        Task Handle(TEvent @event);
    }

    public interface IDynamicIntegrationEventHandler
    {
        Task Handle(object eventData);
    }
}
Source: Core.Application.EventBus/Handlers/IIntegrationEventHandler.cs:3 Type Parameters:
  • TEvent - The integration event type to handle
Methods:
  • Task Handle(TEvent @event) - Processes the integration event

Publisher Handler Example

Handler that publishes integration events to external systems.
using Core.Application;

namespace Application
{
    public class DummyEntityCreatedIntegrationEventHandlerPub : IIntegrationEventHandler<DummyEntityCreatedIntegrationEvent>
    {
        private readonly IEventBus _eventBus;

        public DummyEntityCreatedIntegrationEventHandlerPub(IEventBus eventBus)
        {
            _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
        }

        public Task Handle(DummyEntityCreatedIntegrationEvent @event)
        {
            // Publish to external event bus (RabbitMQ, Azure Service Bus, etc.)
            _eventBus.Publish(@event);

            return Task.CompletedTask;
        }
    }
}
Source: Application/Integrations/Handlers/Publishers/DummyEntityCreatedIntegrationEventHandlerPub.cs:5

Event Bus Interface

namespace Core.Application
{
    public interface IEventBus
    {
        void Publish(IntegrationEvent @event);

        void Subscribe<TEvent, THandler>()
            where TEvent : IntegrationEvent
            where THandler : IIntegrationEventHandler<TEvent>;

        void SubscribeDynamic<THandler>(string eventName)
            where THandler : IDynamicIntegrationEventHandler;

        void Unsubscribe<TEvent, THandler>()
            where TEvent : IntegrationEvent
            where THandler : IIntegrationEventHandler<TEvent>;

        void UnsubscribeDynamic<THandler>(string eventName)
            where THandler : IDynamicIntegrationEventHandler;
    }
}
Source: Core.Application.EventBus/Buses/IEventBus.cs:3 Methods:
  • Publish() - Publishes an integration event to subscribers
  • Subscribe<TEvent, THandler>() - Registers a handler for an event type
  • Unsubscribe<TEvent, THandler>() - Removes a handler registration
  • SubscribeDynamic() - Registers a dynamic handler by event name
  • UnsubscribeDynamic() - Removes a dynamic handler

Event Handling Lifecycle

Domain Event Flow

  1. Command Execution - Handler processes command and persists entity
  2. Event Creation - Map entity to domain event using entity.To<TEvent>()
  3. Domain Event Publishing - Publish via ICommandQueryBus.Publish()
  4. Event Handler Execution - MediatR routes to registered handlers
  5. Integration Event Publishing - Handlers optionally publish to IEventBus
  6. External Distribution - Event bus sends to message broker

Integration Event Flow

Command Query Bus

ICommandQueryBus Interface

Used for publishing domain events within the bounded context.
using MediatR;

namespace Core.Application
{
    public interface ICommandQueryBus
    {
        Task Publish<TNotification>(TNotification notification, CancellationToken cancellationToken = default) 
            where TNotification : INotification;
        
        Task Send(IRequest request, CancellationToken cancellationToken = default);
        
        Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default);
    }
}
Source: Core.Application.ComandQueryBus/Buses/ICommandQueryBus.cs:5 Methods:
  • Publish<TNotification>() - Publishes domain events (notifications)
  • Send() - Sends commands/queries without return value
  • Send<TResponse>() - Sends commands/queries with return value

Best Practices

  • Use past tense: EntityCreated, OrderShipped, PaymentProcessed
  • Be specific and descriptive
  • Domain events: [Entity][Action] (e.g., DummyEntityCreated)
  • Integration events: Append IntegrationEvent suffix
Use Domain Events when:
  • Communication within same bounded context
  • Triggering immediate side effects (e.g., sending email)
  • Updating read models or projections
  • Business logic coordination
Use Integration Events when:
  • Communication between bounded contexts
  • Cross-service messaging
  • Event sourcing
  • Asynchronous processing
  • Include all data needed by subscribers
  • Avoid including sensitive information
  • Keep events immutable (private setters)
  • Version events for backward compatibility
  • Domain events: Include entity state
  • Integration events: Use Data property for payload
  • Publish domain events AFTER persistence succeeds
  • Use transactions to ensure consistency
  • Pass cancellationToken for cancellation support
  • Handle publishing failures gracefully
  • Consider idempotency for integration events
  • Keep handlers focused and simple
  • Handlers should be idempotent
  • Use separate handlers for different concerns
  • Don’t throw exceptions for non-critical failures
  • Log handler execution for debugging

Event Types Comparison

AspectDomain EventIntegration Event
ScopeBounded contextCross-service
TransportMediatR (in-process)Message broker
Base ClassDomainEventIntegrationEvent
InterfaceIRequestNotificationIntegrationEvent
PublisherICommandQueryBusIEventBus
SerializationNot serializedJSON serialized
TimingSynchronousAsynchronous
Use CaseInternal coordinationExternal integration

Build docs developers (and LLMs) love