blogs/DotnetDomainDrivenDesign
View on GitHub
C#

DDD Learning Repository: Order Fulfillment with Event Sourcing

A production-style, staff-level .NET 10 learning repository that deeply teaches Domain-Driven Design (DDD) and Event Sourcing through a realistic Order Fulfillment domain.

This is not a toy project. It is a serious learning repository with real code, strong architecture, deep documentation, Mermaid diagrams, and clear tradeoff discussions.


Why Order Fulfillment?

Order Fulfillment is an ideal domain for learning DDD + Event Sourcing because:

  • Rich aggregate rules: Orders have complex state transitions (Draft → Submitted → InventoryReserved → PaymentConfirmed → Shipped → Delivered) with strict invariants at each step
  • Natural domain events: Every state change is a meaningful business event (OrderCreated, PaymentConfirmed, OrderShipped) — not just CRUD operations
  • Value of history: The full event timeline has real business value (auditing, dispute resolution, analytics)
  • Multiple bounded contexts: Ordering and Inventory are naturally separate contexts that must integrate
  • Eventual consistency: Inventory reservation, projection updates, and cross-context communication are naturally eventually consistent
  • Caching opportunities: Order status lookups, dashboard stats, and timeline views benefit from caching
  • Read model diversity: Current state, timeline history, and dashboard aggregations are naturally different projections from the same events

Architecture Overview

text
┌─────────────────────────────────────────────────────────┐
│                    API (ASP.NET Core)                     │
│              Commands (POST) │ Queries (GET)              │
└──────────┬──────────────────┬───────────────────────────┘
           │                  │
    ┌──────▼──────┐   ┌──────▼──────┐
    │ Application  │   │   Read      │
    │  Handlers    │   │  Repository │
    │ (MediatR)    │   │  (Dapper)   │
    └──────┬──────┘   └──────┬──────┘
           │                  │
    ┌──────▼──────┐   ┌──────▼──────┐
    │   Domain     │   │  PostgreSQL  │
    │  Aggregates  │   │  Read Models │
    └──────┬──────┘   └─────────────┘
           │                  ▲
    ┌──────▼──────┐           │
    │ Event Store  │   ┌──────┴──────┐
    │ (PostgreSQL) ├──►│ Projections  │
    └──────┬──────┘   └─────────────┘
           │                  ▲
    ┌──────▼──────┐   ┌──────┴──────┐
    │    Kafka     │   │  Background  │
    │ Integration  │   │   Workers    │
    │   Events     │   │  (Polling)   │
    └─────────────┘   └─────────────┘
           │
    ┌──────▼──────┐
    │    Redis     │
    │  Snapshots   │
    │   Cache      │
    │ Idempotency  │
    └─────────────┘

Tech Stack

TechnologyRoleWhy
.NET 10Runtime & APILatest platform, modern C# features
PostgreSQLEvent Store + Read ModelsACID for events, flexible JSONB, proven at scale
KafkaIntegration EventsDurable, ordered, partitioned event distribution between contexts
RedisCache + Snapshots + IdempotencyFast lookups, TTL-based expiry, atomic operations
Docker ComposeLocal DevelopmentReproducible environment with all dependencies

Project Structure

text
├── src/
│   ├── DddLearning.Domain/              # Pure domain model — no dependencies
│   │   ├── SharedKernel/                 # Base classes: AggregateRoot, Entity, ValueObject
│   │   ├── Ordering/                     # Ordering bounded context
│   │   │   ├── Aggregates/Order.cs       # Main aggregate root with event sourcing
│   │   │   ├── Entities/OrderLine.cs     # Entity within Order aggregate
│   │   │   ├── ValueObjects/             # Money, Address, OrderStatus, etc.
│   │   │   ├── Events/                   # Domain events emitted by Order
│   │   │   ├── Services/                 # Domain services (pricing)
│   │   │   ├── Factories/                # Complex creation logic
│   │   │   └── Specifications/           # Business rule specifications
│   │   └── Inventory/                    # Inventory bounded context
│   │       ├── Aggregates/StockItem.cs   # Inventory aggregate
│   │       ├── Events/                   # Stock domain events
│   │       └── ValueObjects/             # SKU, StockLevel
│   │
│   ├── DddLearning.Contracts/           # Shared contracts — commands, queries, DTOs
│   │   ├── Commands/                     # Command records (intent to change state)
│   │   ├── Queries/                      # Query records (intent to read state)
│   │   ├── Events/                       # Integration events (cross-context)
│   │   └── Responses/                    # Response DTOs
│   │
│   ├── DddLearning.Application/         # Application services — orchestration
│   │   ├── Orders/Commands/              # Command handlers (MediatR)
│   │   ├── Orders/Queries/               # Query handlers
│   │   ├── Behaviors/                    # Cross-cutting (logging, validation)
│   │   └── Interfaces/                   # Port interfaces (event store, cache, etc.)
│   │
│   ├── DddLearning.Infrastructure/      # Adapters — PostgreSQL, Redis, Kafka
│   │   ├── EventStore/                   # PostgreSQL event store implementation
│   │   ├── Repositories/                 # Event-sourced aggregate repositories
│   │   ├── Kafka/                        # Integration event publisher/consumer
│   │   ├── Redis/                        # Cache, snapshots, idempotency
│   │   └── Persistence/                  # Read model repository, DB init
│   │
│   ├── DddLearning.Projections/         # Event → Read Model projections
│   │   ├── OrderProjections/             # Order read model, timeline, dashboard
│   │   ├── ReadModels/                   # POCO read model classes
│   │   └── Handlers/                     # Projection dispatcher and rebuilder
│   │
│   ├── DddLearning.Api/                 # ASP.NET Core Web API
│   │   ├── Endpoints/                    # Minimal API endpoints
│   │   └── Middleware/                   # Exception handling
│   │
│   └── DddLearning.BackgroundWorkers/   # Hosted services
│       └── Workers/                      # Event polling, Kafka consumer
│
├── tests/
│   ├── DddLearning.UnitTests/           # Aggregate, value object, handler tests
│   ├── DddLearning.IntegrationTests/    # API and infrastructure tests
│   └── DddLearning.ArchitectureTests/   # Dependency rule enforcement
│
├── docs/                                 # Deep documentation with Mermaid diagrams
├── docker/                               # Dockerfiles and init scripts
└── docker-compose.yml                    # Full local environment

Quick Start

Prerequisites

  • Docker and Docker Compose
  • .NET 10 SDK (for local development without Docker)

Run Everything

bash
docker compose up --build

Access

Demo Flow

  1. Create an order:
bash
curl -X POST http://localhost:5000/api/orders \
  -H "Content-Type: application/json" \
  -d '{"customerId": "550e8400-e29b-41d4-a716-446655440000", "street": "123 Main St", "city": "Seattle", "state": "WA", "zipCode": "98101", "country": "US"}'
  1. Add a line item (use the order ID from step 1):
bash
curl -X POST http://localhost:5000/api/orders/{orderId}/lines \
  -H "Content-Type: application/json" \
  -d '{"productId": "660e8400-e29b-41d4-a716-446655440001", "quantity": 2, "amount": 29.99, "currency": "USD"}'
  1. Submit the order:
bash
curl -X POST http://localhost:5000/api/orders/{orderId}/submit
  1. Reserve inventory:
bash
curl -X POST http://localhost:5000/api/orders/{orderId}/reserve-inventory
  1. Confirm payment:
bash
curl -X POST http://localhost:5000/api/orders/{orderId}/confirm-payment \
  -H "Content-Type: application/json" \
  -d '{"paymentReference": "PAY-12345"}'
  1. Ship the order:
bash
curl -X POST http://localhost:5000/api/orders/{orderId}/ship \
  -H "Content-Type: application/json" \
  -d '{"trackingNumber": "TRACK-67890"}'
  1. View the order:
bash
curl http://localhost:5000/api/orders/{orderId}
  1. View the event timeline:
bash
curl http://localhost:5000/api/orders/{orderId}/timeline
  1. View the dashboard:
bash
curl http://localhost:5000/api/orders/dashboard

What You'll Learn

Domain-Driven Design

  • Strategic Design: Bounded contexts, context mapping, ubiquitous language, subdomains
  • Tactical Design: Entities, value objects, aggregates, repositories, factories, domain services, specifications
  • Aggregate Design: Boundaries, invariants, consistency, transaction scope, size tradeoffs
  • Rich Domain Model: Business rules in the domain, not in services or controllers

Event Sourcing

  • Fundamentals: Events as source of truth, append-only streams, aggregate reconstruction
  • Mechanics: Optimistic concurrency, versioning, serialization, snapshots
  • Projections: Read model generation, catch-up subscriptions, rebuild capability
  • Tradeoffs: When to use, when to avoid, complexity costs

Infrastructure Patterns

  • Kafka: Integration event distribution, partition key strategy, idempotent consumers
  • PostgreSQL: Event store design, read model tables, transactional guarantees
  • Redis: Caching projections, snapshot storage, idempotency keys

Key Design Decisions

DecisionChoiceRationale
Event StorePostgreSQL + custom tablesEducational clarity; production would consider EventStoreDB or Marten
CQRS MediatorMediatRClear command/query separation, pipeline behaviors
ORMDapper (not EF Core)Explicit SQL for event store, educational value
Integration EventsKafkaDurable, ordered, partitioned; industry standard
CachingRedisFast, TTL support, atomic operations
SnapshotsRedisLow-latency retrieval, natural TTL expiry

Documentation

See the /docs folder for deep, staff-level documentation:


Testing

bash
# Unit tests (no infrastructure needed)
dotnet test tests/DddLearning.UnitTests

# Architecture tests (no infrastructure needed)
dotnet test tests/DddLearning.ArchitectureTests

# Integration tests (requires Docker infrastructure)
docker compose up -d postgres redis kafka
dotnet test tests/DddLearning.IntegrationTests

License

This is a learning repository. Use it to deepen your understanding of DDD, Event Sourcing, and distributed systems architecture.