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

307 lines
7.6 KiB
Markdown

# 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)**
```csharp
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**
```csharp
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**
```csharp
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
```csharp
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
```csharp
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
```csharp
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
- [02-domain-events.md](02-domain-events.md) - Define domain events in detail
- [03-commands.md](03-commands.md) - Implement commands and handlers
- [04-queries.md](04-queries.md) - Build queries and projections
## See Also
- [Event Sourcing Tutorial](../event-sourcing/README.md)
- [Sagas](../../event-streaming/sagas/README.md)
- [Best Practices](../../best-practices/README.md)