Interface IDomainEvent
- Namespace
- FunctionalDdd
- Assembly
- FunctionalDdd.DomainDrivenDesign.dll
Represents a domain event - a record of something significant that happened in the business domain. Domain events capture state changes and business occurrences that domain experts care about.
public interface IDomainEvent
- Extension Methods
Examples
// Define domain events as immutable records with OccurredAt as the timestamp
public record OrderCreated(OrderId OrderId, CustomerId CustomerId, DateTime OccurredAt) : IDomainEvent;
public record OrderSubmitted(OrderId OrderId, Money Total, DateTime OccurredAt) : IDomainEvent;
// Raise events from an aggregate
public class Order : Aggregate<OrderId>
{
private Order(OrderId id, CustomerId customerId) : base(id)
{
CustomerId = customerId;
CreatedAt = DateTime.UtcNow;
DomainEvents.Add(new OrderCreated(id, customerId, DateTime.UtcNow));
}
public Result<Order> Submit()
{
return this.ToResult()
.Ensure(_ => Status == OrderStatus.Draft, Error.Validation("Wrong status"))
.Tap(_ =>
{
Status = OrderStatus.Submitted;
SubmittedAt = DateTime.UtcNow;
DomainEvents.Add(new OrderSubmitted(Id, Total, DateTime.UtcNow));
});
}
}
// Handle the event
public class OrderSubmittedHandler
{
public async Task Handle(OrderSubmitted evt, CancellationToken ct)
{
_logger.LogInformation("Order {OrderId} submitted at {OccurredAt}",
evt.OrderId, evt.OccurredAt);
await _emailService.SendOrderConfirmationAsync(evt.OrderId, ct);
}
}
Remarks
Domain events are a key tactical pattern in Domain-Driven Design that enable:
- Loose coupling between aggregates and bounded contexts
- Temporal decoupling of side effects from core business logic
- Event sourcing and audit trails
- Integration between microservices
- Communication of state changes to external systems
Best practices for domain events:
- Name events in the past tense (e.g., OrderSubmitted, PaymentProcessed)
- Make events immutable - use readonly properties or init-only setters
- Include all relevant data needed by handlers to avoid querying
- Keep events focused on domain concepts, not technical implementation
- Use OccurredAt for the event timestamp - avoid redundant timestamp fields
Domain events vs. Integration events:
- Domain events: Internal to the bounded context, raised by aggregates
- Integration events: Published across bounded contexts, may be derived from domain events
Properties
OccurredAt
Gets the UTC timestamp when this domain event occurred.
DateTime OccurredAt { get; }
Property Value
- DateTime
The date and time in UTC when the event was raised.
Remarks
This timestamp represents when the business action occurred, not when the event was persisted or published. Always use UTC to ensure consistency across distributed systems and time zones.
Use OccurredAt as the single timestamp for your events - avoid adding redundant fields like
CreatedAt, SubmittedAt, etc. that duplicate this information:
// Good - OccurredAt captures when the event happened
public record OrderSubmitted(OrderId OrderId, Money Total, DateTime OccurredAt) : IDomainEvent;
// Avoid - redundant SubmittedAt duplicates OccurredAt
public record OrderSubmitted(OrderId OrderId, Money Total, DateTime SubmittedAt, DateTime OccurredAt) : IDomainEvent;