Domain Events Generator
Overview
Domain Events capture something significant that happened in the domain:
- Raised by entities - When state changes occur
- Handled by notification handlers - React to events
- Outbox pattern - Reliable event delivery
- Decoupled - Publisher doesn't know subscribers
Quick Reference
| Component | Purpose | Location |
|---|---|---|
IDomainEvent | Marker interface | Domain/Abstractions |
{Entity}{Action}DomainEvent | Event record | Domain/{Aggregate}/Events |
{Event}DomainEventHandler | Event handler | Application/{Feature} |
OutboxMessage | Persisted event | Infrastructure/Outbox |
Event Structure
/Domain/
├── Abstractions/
│ └── IDomainEvent.cs
└── {Aggregate}/
└── Events/
├── {Entity}CreatedDomainEvent.cs
├── {Entity}UpdatedDomainEvent.cs
└── ...
/Application/
└── {Feature}/
└── EventHandlers/
├── {Event}Handler.cs
└── ...
/Infrastructure/
└── Outbox/
├── OutboxMessage.cs
├── OutboxMessageConfiguration.cs
└── ProcessOutboxMessagesJob.cs
Template: Domain Event Interface
// src/{name}.domain/Abstractions/IDomainEvent.cs
using MediatR;
namespace {name}.domain.abstractions;
/// <summary>
/// Marker interface for domain events.
/// Domain events represent something significant that happened in the domain.
/// </summary>
public interface IDomainEvent : INotification
{
/// <summary>
/// Unique identifier for this event instance
/// </summary>
Guid Id { get; }
/// <summary>
/// When the event occurred
/// </summary>
DateTime OccurredOnUtc { get; }
}
Template: Base Domain Event Record
// src/{name}.domain/Abstractions/DomainEvent.cs
namespace {name}.domain.abstractions;
/// <summary>
/// Base record for domain events with common properties
/// </summary>
public abstract record DomainEvent : IDomainEvent
{
public Guid Id { get; init; } = Guid.NewGuid();
public DateTime OccurredOnUtc { get; init; } = DateTime.UtcNow;
}
Template: Specific Domain Events
// src/{name}.domain/{Aggregate}/Events/{Entity}CreatedDomainEvent.cs
using {name}.domain.abstractions;
namespace {name}.domain.{aggregate}.events;
/// <summary>
/// Raised when a new {Entity} is created
/// </summary>
public sealed record {Entity}CreatedDomainEvent(
Guid {Entity}Id) : DomainEvent;
// src/{name}.domain/{Aggregate}/Events/{Entity}UpdatedDomainEvent.cs
/// <summary>
/// Raised when a {Entity} is updated
/// </summary>
public sealed record {Entity}UpdatedDomainEvent(
Guid {Entity}Id,
string PropertyName,
string? OldValue,
string? NewValue) : DomainEvent;
// src/{name}.domain/{Aggregate}/Events/{Entity}DeactivatedDomainEvent.cs
/// <summary>
/// Raised when a {Entity} is deactivated
/// </summary>
public sealed record {Entity}DeactivatedDomainEvent(
Guid {Entity}Id,
string Reason) : DomainEvent;
// src/{name}.domain/{Aggregate}/Events/{Entity}DeletedDomainEvent.cs
/// <summary>
/// Raised when a {Entity} is deleted
/// </summary>
public sealed record {Entity}DeletedDomainEvent(
Guid {Entity}Id) : DomainEvent;
Template: Rich Domain Events
// src/{name}.domain/Users/Events/UserRegisteredDomainEvent.cs
using {name}.domain.abstractions;
namespace {name}.domain.users.events;
/// <summary>
/// Raised when a new user registers
/// </summary>
public sealed record UserRegisteredDomainEvent : DomainEvent
{
public Guid UserId { get; init; }
public string Email { get; init; } = string.Empty;
public string Name { get; init; } = string.Empty;
public Guid OrganizationId { get; init; }
public UserRegisteredDomainEvent(
Guid userId,
string email,
string name,
Guid organizationId)
{
UserId = userId;
Email = email;
Name = name;
OrganizationId = organizationId;
}
}
// src/{name}.domain/Assessments/Events/AssessmentCompletedDomainEvent.cs
/// <summary>
/// Raised when a user completes an assessment
/// </summary>
public sealed record AssessmentCompletedDomainEvent : DomainEvent
{
public Guid AssessmentId { get; init; }
public Guid UserId { get; init; }
public Guid OrganizationId { get; init; }
public string AssessmentType { get; init; } = string.Empty;
public decimal Score { get; init; }
public DateTime CompletedAt { get; init; }
public AssessmentCompletedDomainEvent(
Guid assessmentId,
Guid userId,
Guid organizationId,
string assessmentType,
decimal score,
DateTime completedAt)
{
AssessmentId = assessmentId;
UserId = userId;
OrganizationId = organizationId;
AssessmentType = assessmentType;
Score = score;
CompletedAt = completedAt;
}
}
Template: Raising Events in Entity
// src/{name}.domain/{Aggregate}/{Entity}.cs
using {name}.domain.abstractions;
using {name}.domain.{aggregate}.events;
namespace {name}.domain.{aggregate};
public sealed class {Entity} : Entity
{
// ... properties
private {Entity}(
Guid id,
string name,
Guid organizationId,
DateTime createdAt)
: base(id)
{
Name = name;
OrganizationId = organizationId;
CreatedAt = createdAt;
}
/// <summary>
/// Factory method - raises Created event
/// </summary>
public static Result<{Entity}> Create(
string name,
Guid organizationId,
DateTime createdAt)
{
// Validation...
var {entity} = new {Entity}(
Guid.NewGuid(),
name,
organizationId,
createdAt);
// Raise domain event
{entity}.RaiseDomainEvent(new {Entity}CreatedDomainEvent({entity}.Id));
return {entity};
}
/// <summary>
/// Update method - raises Updated event
/// </summary>
public Result Update(string name, DateTime updatedAt)
{
if (string.IsNullOrWhiteSpace(name))
{
return Result.Failure({Entity}Errors.NameRequired);
}
var oldName = Name;
Name = name;
UpdatedAt = updatedAt;
// Raise domain event with change details
RaiseDomainEvent(new {Entity}UpdatedDomainEvent(
Id,
nameof(Name),
oldName,
name));
return Result.Success();
}
/// <summary>
/// Deactivate method - raises Deactivated event
/// </summary>
public Result Deactivate(string reason, DateTime deactivatedAt)
{
if (!IsActive)
{
return Result.Failure({Entity}Errors.AlreadyDeactivated);
}
IsActive = false;
UpdatedAt = deactivatedAt;
RaiseDomainEvent(new {Entity}DeactivatedDomainEvent(Id, reason));
return Result.Success();
}
}
Template: Domain Event Handler
// src/{name}.application/{Feature}/EventHandlers/{Entity}CreatedDomainEventHandler.cs
using MediatR;
using Microsoft.Extensions.Logging;
using {name}.domain.{aggregate}.events;
namespace {name}.application.{feature}.eventhandlers;
/// <summary>
/// Handles {Entity}CreatedDomainEvent
/// </summary>
internal sealed class {Entity}CreatedDomainEventHandler
: INotificationHandler<{Entity}CreatedDomainEvent>
{
private readonly ILogger<{Entity}CreatedDomainEventHandler> _logger;
public {Entity}CreatedDomainEventHandler(
ILogger<{Entity}CreatedDomainEventHandler> logger)
{
_logger = logger;
}
public Task Handle(
{Entity}CreatedDomainEvent notification,
CancellationToken cancellationToken)
{
_logger.LogInformation(
"{Entity} created: {EntityId} at {OccurredOn}",
notification.{Entity}Id,
notification.OccurredOnUtc);
/