dotnet-cqrs/docs/tutorials/ecommerce-example/02-domain-events.md

4.5 KiB

E-Commerce Example: Domain Events

Define and implement domain events for the e-commerce order system.

Event Design Principles

All events in this system follow these principles:

Past tense naming - Events describe what happened Immutable records - Events cannot be changed once created Complete context - Events include all relevant data Business language - Events use domain terminology

Order Lifecycle Events

OrderPlacedEvent

Emitted when a customer places a new order.

public record OrderPlacedEvent
{
    public string OrderId { get; init; } = string.Empty;
    public string CustomerId { get; init; } = string.Empty;
    public string CustomerName { get; init; } = string.Empty;
    public string CustomerEmail { get; init; } = string.Empty;
    public List<OrderLineDto> Lines { get; init; } = new();
    public decimal TotalAmount { get; init; }
    public DateTimeOffset PlacedAt { get; init; }
}

public record OrderLineDto
{
    public string ProductId { get; init; } = string.Empty;
    public string ProductName { get; init; } = string.Empty;
    public int Quantity { get; init; }
    public decimal UnitPrice { get; init; }
    public decimal LineTotal { get; init; }
}

When to emit: After order validation passes Stream: order-{orderId} Triggers: Inventory reservation, payment processing

OrderPaidEvent

Emitted when payment for an order is successfully processed.

public record OrderPaidEvent
{
    public string OrderId { get; init; } = string.Empty;
    public string PaymentId { get; init; } = string.Empty;
    public string PaymentMethod { get; init; } = string.Empty;
    public decimal Amount { get; init; }
    public DateTimeOffset PaidAt { get; init; }
}

When to emit: After successful payment processing Stream: order-{orderId} Triggers: Shipment creation

OrderShippedEvent

Emitted when an order is shipped to the customer.

public record OrderShippedEvent
{
    public string OrderId { get; init; } = string.Empty;
    public string ShipmentId { get; init; } = string.Empty;
    public string TrackingNumber { get; init; } = string.Empty;
    public string Carrier { get; init; } = string.Empty;
    public DateTimeOffset EstimatedDelivery { get; init; }
    public DateTimeOffset ShippedAt { get; init; }
}

When to emit: When warehouse marks order as shipped Stream: order-{orderId} Triggers: Email notification, tracking update

OrderDeliveredEvent

Emitted when an order is delivered to the customer.

public record OrderDeliveredEvent
{
    public string OrderId { get; init; } = string.Empty;
    public string ShipmentId { get; init; } = string.Empty;
    public string SignedBy { get; init; } = string.Empty;
    public DateTimeOffset DeliveredAt { get; init; }
}

When to emit: When carrier confirms delivery Stream: order-{orderId} Triggers: Completion email

OrderCancelledEvent

Emitted when an order is cancelled.

public record OrderCancelledEvent
{
    public string OrderId { get; init; } = string.Empty;
    public string CancelledBy { get; init; } = string.Empty;
    public string Reason { get; init; } = string.Empty;
    public bool RefundIssued { get; init; }
    public DateTimeOffset CancelledAt { get; init; }
}

When to emit: When customer or admin cancels order Stream: order-{orderId} Triggers: Inventory release, refund processing

Event Stream Design

Each order has its own stream containing all lifecycle events:

Stream: order-12345
├── Offset 1: OrderPlacedEvent
├── Offset 2: InventoryReservedEvent
├── Offset 3: PaymentProcessedEvent
├── Offset 4: OrderPaidEvent
├── Offset 5: ShipmentCreatedEvent
└── Offset 6: OrderShippedEvent

Event Registration

Register events with the event store:

// In Program.cs
builder.Services.AddEventStreaming()
    .AddPostgresEventStore(builder.Configuration.GetConnectionString("EventStore"));

// Register event handlers
builder.Services.AddEventHandler<OrderPlacedEvent, OrderPlacedEventHandler>();
builder.Services.AddEventHandler<OrderPaidEvent, OrderPaidEventHandler>();
builder.Services.AddEventHandler<OrderShippedEvent, OrderShippedEventHandler>();

See Also