Event Sourcing Fundamentals
What event sourcing actually is and when you should use it
Event Sourcing Fundamentals
Section titled “Event Sourcing Fundamentals”Event sourcing isn’t rocket science, but most explanations make it sound like it. Here’s what it actually is: instead of storing what your data looks like now, you store what happened to it.
What Is Event Sourcing?
Section titled “What Is Event Sourcing?”Think of your bank account. Your bank doesn’t just store “John has $500.” They store every transaction: deposit $1000, withdraw $200, pay rent $300. Your current balance is calculated from that history.
That’s event sourcing. You store the events (what happened) instead of the current state (what things look like now).
[Diagram Placeholder: Traditional vs Event Sourcing Storage] Traditional: User table with current values Event Sourcing: Stream of UserRegistered, EmailChanged, ProfileUpdated events
Events as Source of Truth
Section titled “Events as Source of Truth”Events are facts about what happened in your business:
OrderPlaced
- Someone ordered pizzaPaymentProcessed
- Their card was chargedOrderDelivered
- Pizza reached the customer
These events are:
- Immutable - You can’t change what happened
- Append-only - New events get added, old ones stay
- Timestamped - You know when everything happened
Event Streams and Aggregates
Section titled “Event Streams and Aggregates”Events are grouped into streams. Usually one stream per business entity:
order-123
stream contains all events for order 123user-456
stream contains all events for user 456
An aggregate is just the current state built from all events in a stream.
[Diagram Placeholder: Event Stream to Aggregate] Stream: [OrderPlaced, ItemAdded, ItemRemoved, OrderConfirmed] → Current Order State
Event Sourcing vs Traditional CRUD
Section titled “Event Sourcing vs Traditional CRUD”Traditional CRUD
Section titled “Traditional CRUD”// Update user emailawait db.users.update(userId, { email: 'new@email.com' });// Old email is gone forever
Event Sourcing
Section titled “Event Sourcing”// Record what happenedawait eventStore.append('user-123', { type: 'EmailChanged', data: { oldEmail: 'old@email.com', newEmail: 'new@email.com', reason: 'user_request' }});// History is preserved
[Diagram Placeholder: CRUD vs Event Sourcing Data Flow]
When Event Sourcing Makes Sense
Section titled “When Event Sourcing Makes Sense”Use event sourcing when:
- Audit trails matter - Financial systems, healthcare, legal
- Business processes are complex - E-commerce, logistics, workflows
- You need to replay scenarios - Debugging, analytics, compliance
- Temporal queries are important - “What did our inventory look like last Tuesday?”
Don’t use event sourcing when:
- Simple CRUD is enough - Basic user profiles, settings
- Performance is critical - High-frequency trading, real-time games
- Your team isn’t ready - Event sourcing adds complexity
- Data rarely changes - Reference data, configuration
Common Use Cases
Section titled “Common Use Cases”E-commerce Orders Every step matters: placed, paid, shipped, delivered, returned. You need the full story for customer service and analytics.
Financial Transactions Banks don’t just store balances. Every deposit, withdrawal, and transfer is recorded. Regulations require it.
Collaborative Editing Google Docs doesn’t store the current document. It stores every keystroke, every change. That’s how real-time collaboration works.
Common Anti-Patterns
Section titled “Common Anti-Patterns”Don’t event source everything - Your user’s favorite color doesn’t need an audit trail.
Don’t make events too granular - MouseMoved
events will kill your performance.
Don’t ignore snapshots - Replaying 10 million events to get current state is slow.
Don’t forget about deletes - GDPR compliance is tricky with immutable events.
Benefits and Trade-offs
Section titled “Benefits and Trade-offs”What You Get
Section titled “What You Get”Complete audit trail - You know exactly what happened and when.
Time travel - Query your system as it existed at any point in history.
Debugging superpowers - Replay events to reproduce bugs.
Business insights - Analyze how your business actually works.
Scalable reads - Build multiple projections for different use cases.
What You Pay
Section titled “What You Pay”Complexity - More moving parts than simple CRUD.
Storage - Events accumulate over time.
Learning curve - Your team needs to think differently.
Eventual consistency - Projections might lag behind events.
Tooling - You need an event store, not just a database.
Implementation Patterns
Section titled “Implementation Patterns”Basic Event Store Operations
Section titled “Basic Event Store Operations”// Append events to a streamawait eventStore.append('order-123', [ { type: 'OrderPlaced', data: { items: ['pizza'], total: 15.99 } }, { type: 'PaymentProcessed', data: { amount: 15.99, method: 'card' } }]);
// Read events from a streamconst events = await eventStore.readStream('order-123');
// Build current state from eventsconst currentOrder = events.reduce(orderReducer, initialState);
Handling Concurrency
Section titled “Handling Concurrency”Event stores use optimistic concurrency control:
// This will fail if someone else modified the streamawait eventStore.append('order-123', newEvents, { expectedVersion: 5});
[Diagram Placeholder: Optimistic Concurrency Control]
Getting Started
Section titled “Getting Started”Start small. Pick one aggregate that changes frequently and has business value. Don’t try to event source your entire system on day one.
Good first candidates:
- Shopping carts
- Order processing
- User registration flows
- Document workflows
Build it, learn from it, then expand.
Event sourcing isn’t magic. It’s just a different way to think about data. Sometimes it’s exactly what you need. Sometimes it’s overkill. The key is knowing the difference.