Event Sourcing in .NET (0 → Expert)
This is a comprehensive, end-to-end reference for implementing modern Event Sourcing in .NET.
If you read it in order and type the code as you go, you'll end up with:
- A correct mental model (what ES is and what it isn't)
- A clean event-sourced aggregate (business rules enforced in one place)
- An append-only event store API (with optimistic concurrency)
- A projection/read model pipeline (CQRS-ready, rebuildable)
- A pragmatic integration story (outbox + integration events)
- A production checklist (evolution, idempotency, ops)
- Expert-level patterns (sagas, temporal queries, GDPR, scaling)
TL;DR definitions
- Event Sourcing: You store facts ("what happened") as immutable events and rebuild state by replaying them.
- Aggregate: The boundary where you enforce invariants (business rules) and decide which events to emit.
- Stream: The ordered event sequence for a single aggregate instance (e.g.,
bank-account-{id}). - Projection: A derived view built from events (often stored in a "read model" store).
- Optimistic concurrency: "Append only if stream version == expected version" to prevent lost updates.
Who should use Event Sourcing?
Use it when at least one of these is true:
- You need a perfect audit trail of how state changed (not just current state).
- You need rebuildable read models (and can tolerate eventual consistency).
- Your domain has complex invariants and you want changes expressed as business facts.
- You'll benefit from "time travel" debugging or retroactive projections ("what was the balance last week?").
Avoid it when:
- Your domain is CRUD/simple and the extra moving parts don't pay for themselves.
- You can't commit to operational discipline (schema evolution, projections rebuilds, idempotency).
Reading path
Part 1: Foundations (Start Here)
- Glossary + mental model
- Aggregates + invariants (the heart)
- Event design + schema (don't skip)
- Event store + concurrency (append correctly)
- Projections + CQRS
- Snapshots
- Integration: outbox + sagas
- Testing
- Operations + evolution
- Real implementations: KurrentDB/EventStoreDB + Orleans
- Pitfalls checklist
Part 2: Expert Topics (Level Up)
- Process Managers + Sagas (orchestrating long-running processes)
- Temporal Queries (time travel for your data)
- Multi-Stream Projections (cross-aggregate views)
- GDPR + Data Privacy (the hardest problem)
- Performance + Scaling
- Anti-Patterns (mistakes that will bite you)
- Event Store Comparison (choosing your storage)
- Advanced Conflict Resolution
- Debugging + Observability
Core example we'll use throughout
We'll use a Bank Account domain because it's small but forces the right thinking:
- You can't withdraw below zero.
- You can't deposit after the account is closed.
- The only "writes" are events:
AccountOpened,MoneyDeposited,MoneyWithdrawn,AccountClosed.
What makes an ES expert?
After completing this guide, you should be able to:
| Skill | Covered In |
|---|---|
| Design aggregates with correct boundaries | Part 1 |
| Handle schema evolution gracefully | Part 1 |
| Build rebuildable projections | Part 1 |
| Coordinate multi-aggregate workflows | Sagas |
| Answer "what was the state at time T?" | Temporal Queries |
| Build cross-aggregate read models | Multi-Stream |
| Handle GDPR deletion requests | GDPR |
| Scale to millions of events | Performance |
| Avoid common mistakes | Anti-Patterns |
| Choose the right event store | Comparison |
| Resolve conflicts intelligently | Conflict Resolution |
| Debug production issues | Observability |
Sources
If you want a second opinion or to align terminology with Microsoft guidance:
https://learn.microsoft.com/en-us/azure/architecture/patterns/event-sourcinghttps://learn.microsoft.com/en-us/azure/architecture/patterns/cqrshttps://learn.microsoft.com/en-us/azure/architecture/databases/guide/transactional-outbox-cosmoshttps://learn.microsoft.com/en-us/dotnet/orleans/grains/event-sourcing/https://learn.microsoft.com/en-us/azure/architecture/reference-architectures/saga/saga