dotnet-cqrs/docs/tutorials/ecommerce-example/01-requirements.md

7.6 KiB

E-Commerce Example: Domain Requirements

Define the requirements for a complete e-commerce order management system.

Business Requirements

Order Management

As a customer, I want to:

  • Place orders with multiple items
  • View my order history
  • Track order status (Placed, Paid, Shipped, Delivered, Cancelled)
  • Cancel orders before shipment
  • Receive email notifications for order events

As an admin, I want to:

  • View all orders
  • Filter orders by status, customer, date range
  • Process payments
  • Mark orders as shipped
  • View order analytics (total revenue, order count)

Inventory Management

As a system, I need to:

  • Reserve inventory when orders are placed
  • Release inventory when orders are cancelled
  • Prevent overselling (no negative inventory)
  • Track inventory levels per product

Payment Processing

As a system, I need to:

  • Charge customers when orders are placed
  • Refund customers when orders are cancelled
  • Handle payment failures gracefully
  • Prevent double-charging

Analytics

As an admin, I want to:

  • View total revenue
  • View order count by status
  • View top-selling products
  • View customer purchase history

Domain Model

Entities

Order (Aggregate Root)

public class Order
{
    public string OrderId { get; set; }
    public string CustomerId { get; set; }
    public List<OrderLine> Lines { get; set; }
    public decimal TotalAmount { get; set; }
    public OrderStatus Status { get; set; }
    public DateTimeOffset PlacedAt { get; set; }
    public DateTimeOffset? PaidAt { get; set; }
    public DateTimeOffset? ShippedAt { get; set; }
    public DateTimeOffset? DeliveredAt { get; set; }
    public DateTimeOffset? CancelledAt { get; set; }
}

public enum OrderStatus
{
    Placed,
    Paid,
    Shipped,
    Delivered,
    Cancelled
}

public class OrderLine
{
    public string ProductId { get; set; }
    public string ProductName { get; set; }
    public int Quantity { get; set; }
    public decimal UnitPrice { get; set; }
    public decimal LineTotal => Quantity * UnitPrice;
}

Customer

public class Customer
{
    public string CustomerId { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public decimal TotalSpent { get; set; }
    public int OrderCount { get; set; }
}

Product

public class Product
{
    public string ProductId { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal Price { get; set; }
    public int AvailableStock { get; set; }
}

Domain Events

Order Events

public record OrderPlacedEvent
{
    public string OrderId { get; init; }
    public string CustomerId { get; init; }
    public List<OrderLineDto> Lines { get; init; }
    public decimal TotalAmount { get; init; }
    public DateTimeOffset PlacedAt { get; init; }
}

public record OrderPaidEvent
{
    public string OrderId { get; init; }
    public string PaymentId { get; init; }
    public decimal Amount { get; init; }
    public DateTimeOffset PaidAt { get; init; }
}

public record OrderShippedEvent
{
    public string OrderId { get; init; }
    public string ShipmentId { get; init; }
    public string TrackingNumber { get; init; }
    public DateTimeOffset ShippedAt { get; init; }
}

public record OrderDeliveredEvent
{
    public string OrderId { get; init; }
    public DateTimeOffset DeliveredAt { get; init; }
}

public record OrderCancelledEvent
{
    public string OrderId { get; init; }
    public string Reason { get; init; }
    public DateTimeOffset CancelledAt { get; init; }
}

Inventory Events

public record InventoryReservedEvent
{
    public string OrderId { get; init; }
    public string ReservationId { get; init; }
    public List<InventoryItemDto> Items { get; init; }
    public DateTimeOffset ReservedAt { get; init; }
}

public record InventoryReleasedEvent
{
    public string ReservationId { get; init; }
    public List<InventoryItemDto> Items { get; init; }
    public DateTimeOffset ReleasedAt { get; init; }
}

public record InventoryDepletedEvent
{
    public string ProductId { get; init; }
    public DateTimeOffset DepletedAt { get; init; }
}

Payment Events

public record PaymentProcessedEvent
{
    public string PaymentId { get; init; }
    public string OrderId { get; init; }
    public decimal Amount { get; init; }
    public DateTimeOffset ProcessedAt { get; init; }
}

public record PaymentRefundedEvent
{
    public string PaymentId { get; init; }
    public string OrderId { get; init; }
    public decimal Amount { get; init; }
    public DateTimeOffset RefundedAt { get; init; }
}

public record PaymentFailedEvent
{
    public string OrderId { get; init; }
    public string Reason { get; init; }
    public DateTimeOffset FailedAt { get; init; }
}

User Stories

Story 1: Place Order

Given I am a customer When I place an order with 2 items Then The system should:

  1. Create an order in "Placed" status
  2. Reserve inventory for the items
  3. Publish OrderPlacedEvent
  4. Send order confirmation email

Story 2: Process Payment

Given An order has been placed When The payment is processed successfully Then The system should:

  1. Update order status to "Paid"
  2. Publish PaymentProcessedEvent and OrderPaidEvent
  3. Send payment confirmation email

Story 3: Ship Order

Given An order has been paid When The order is shipped Then The system should:

  1. Update order status to "Shipped"
  2. Create shipment record with tracking number
  3. Publish OrderShippedEvent
  4. Send shipment notification email

Story 4: Cancel Order

Given An order has been placed but not shipped When The customer cancels the order Then The system should:

  1. Update order status to "Cancelled"
  2. Release reserved inventory
  3. Refund payment if already paid
  4. Publish OrderCancelledEvent
  5. Send cancellation confirmation email

Story 5: View Order History

Given I am a customer When I view my order history Then I should see:

  • All my orders sorted by date
  • Order status for each order
  • Items in each order
  • Order totals

Success Criteria

Functional:

  • Orders can be placed, paid, shipped, and cancelled
  • Inventory is accurately tracked and reserved
  • Payments are processed and refunded correctly
  • Order history is queryable

Non-Functional:

  • System handles 100 orders/second
  • Order history queries return in < 100ms
  • Events are processed in < 1 second
  • System is resilient to failures (retry logic)

Architecture Decisions

CQRS Pattern

  • Commands: PlaceOrder, ProcessPayment, ShipOrder, CancelOrder
  • Queries: GetOrder, ListOrders, GetOrderHistory, GetAnalytics
  • Events: All state changes emit domain events
  • Projections: Order summary, customer analytics, product analytics

Event Sourcing

  • Order aggregate state is rebuilt from events
  • Enables full audit trail
  • Allows rebuilding projections
  • Supports temporal queries

Saga Pattern

  • OrderFulfillmentSaga coordinates order processing:
    1. Reserve inventory
    2. Process payment
    3. Ship order
  • Compensation logic for failures

Next Steps

See Also