dotnet-domain-events-generator
Generates Domain Events and their handlers following DDD patterns. Implements event raising in entities, MediatR notification handlers, and the Outbox pattern for reliable event processing.
git clone --depth 1 https://github.com/ronnythedev/dotnet-clean-architecture-skills /tmp/dotnet-domain-events-generator && cp -r /tmp/dotnet-domain-events-generator/skills/09-dotnet-domain-events-generator ~/.claude/skills/dotnet-domain-events-generatorSKILL.md
# 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
```csharp
// 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
```csharp
// 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
```csharp
// 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
```csharp
// 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
```csharp
// 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 meScaffolds a complete .NET solution following Clean Architecture principles with proper layer separation (API, Application, Domain, Infrastructure). Creates project structure, dependency injection setup, and cross-cutting concerns configuration.
Generates CQRS Commands with Handlers, Validators, and Request DTOs following Clean Architecture patterns. Commands represent actions that modify state and return Result types for proper error handling.
Generates CQRS Queries with Handlers and Response DTOs for read operations. Uses Dapper for optimized read queries, bypassing the domain model for better performance.
Generates Domain Entities following DDD principles with factory methods, private setters, domain events, and proper encapsulation. Supports aggregate roots, child entities, and value objects.
Generates Repository interfaces and implementations following the Repository pattern. Provides data access abstraction for aggregate roots with EF Core implementations.
Generates Entity Framework Core configurations using Fluent API. Maps domain entities to database tables with proper relationships, constraints, and conventions.
Generates RESTful API Controllers with proper routing, versioning, authorization, and MediatR integration. Follows REST conventions and Clean Architecture patterns.
Generates Minimal API endpoints following Microsoft's recommended approach. Creates fast, testable HTTP APIs with minimal code using MapGet/MapPost/MapPut/MapDelete. Preferred over controller-based APIs for new projects.