Skip to content

Why Event Sourcing?

Understanding when event sourcing solves real problems and when it doesn't

Let’s be honest: event sourcing isn’t a silver bullet. It’s a tool that solves specific problems really well, but it’s not right for every application. Here’s when it makes sense and when it doesn’t.

Your app needs to track who did what, when, and why.

Traditional approach: Add audit tables, hope you capture everything, pray you never need to debug what happened.

-- Traditional audit table
CREATE TABLE audit_log (
id SERIAL PRIMARY KEY,
table_name VARCHAR(50),
operation VARCHAR(10),
old_values JSONB,
new_values JSONB,
user_id INT,
created_at TIMESTAMP
);
-- Good luck figuring out the business logic from this

Event sourcing approach: Every business action is an event. The audit trail is the data.

// Every business action is explicitly modeled
const events = [
{ type: 'order.placed', data: { orderId: '123', customerId: 'abc', items: [...] } },
{ type: 'payment.authorized', data: { orderId: '123', amount: 99.99 } },
{ type: 'order.shipped', data: { orderId: '123', trackingNumber: 'xyz' } },
{ type: 'order.cancelled', data: { orderId: '123', reason: 'customer request' } }
];
// The audit trail tells a story

You need to answer questions like “What did this look like yesterday?” or “Show me the state before the bug was introduced.”

Traditional approach: Hope you have backups. Maybe add version columns to everything. Pray it’s enough.

Event sourcing approach: Time travel is built-in.

// What was this user's status before the suspension?
const userBeforeSuspension = await getUserAtTime(userId, '2024-01-15T10:30:00Z');
// Show me all orders that were pending when the system went down
const pendingOrders = await getOrdersAtTime('2024-01-15T14:22:00Z', 'pending');

Your domain has intricate rules that change frequently, and you need to be able to prove compliance.

Traditional approach: Business logic scattered across services, stored procedures, and that one Excel macro the business team maintains.

Event sourcing approach: Business rules are explicit in event handlers. Want to change the rule? Add a new event type.

// Evolution of business rules over time
class OrderPricingRules {
calculatePrice(events: OrderEvent[]): number {
let price = 0;
for (const event of events) {
switch (event.type) {
case 'item.added':
price += event.data.price;
break;
case 'discount.applied':
// Rule change: After 2024-01-01, discounts can't exceed 50%
const maxDiscount = event.createdAt > '2024-01-01' ? 0.5 : 1.0;
const discount = Math.min(event.data.percentage, maxDiscount);
price *= (1 - discount);
break;
case 'tax.calculated':
// Rule change: New tax rates for certain states
const taxRate = this.getTaxRate(event.data.state, event.createdAt);
price += price * taxRate;
break;
}
}
return price;
}
}

You’re building microservices and need reliable communication between them.

Traditional approach: API calls everywhere. Hope services are available. Deal with partial failures. Implement complex retry logic.

Event sourcing approach: Services communicate through events. Eventual consistency is explicit, not accidental.

// Order service publishes events
await eventStore.appendToStream('order-123', [
{ type: 'order.placed', data: { customerId: 'abc', total: 99.99 } }
]);
// Inventory service reacts
eventBus.subscribe('order.placed', async (event) => {
await reserveInventory(event.data.items);
});
// Payment service reacts
eventBus.subscribe('order.placed', async (event) => {
await authorizePayment(event.data.customerId, event.data.total);
});
// If payment fails, inventory automatically gets released
eventBus.subscribe('payment.failed', async (event) => {
await releaseInventory(event.data.orderId);
});

If your app is mostly forms that save to database tables, event sourcing is overkill.

Just use Rails/Django/Laravel:

  • User registration? INSERT into users table.
  • Profile update? UPDATE users SET name = ?
  • Delete account? DELETE FROM users WHERE id = ?

No events needed. No complexity added.

If you’re building a CMS, blog, or content site where 99% of operations are reads, event sourcing adds unnecessary complexity.

Traditional approach is fine:

SELECT title, content, author FROM posts WHERE published = true ORDER BY created_at DESC;

Event sourcing requires understanding distributed systems, eventual consistency, and complex debugging. If your team is learning web development, start simpler.

Start with: Database → API → Frontend Graduate to: Event sourcing when you hit the problems it solves

Event sourcing adds latency. You’re writing events, then building read models from events. If you need sub-millisecond response times, traditional databases are often faster.

  • Complete audit trail: Every change is recorded
  • Time travel: Query any point in history
  • Flexible integration: Services communicate via events
  • Debugging superpowers: Replay scenarios to understand bugs
  • Compliance: Prove what happened and when
  • Simplicity: More moving parts than CRUD
  • Immediate consistency: Read models might be slightly behind
  • Query complexity: Building views from events takes work
  • Learning curve: Your team needs to understand the patterns
  • Eventually consistent reads: Your UI needs to handle this
  • Event versioning: Events schemas will change over time
  • Operational complexity: More infrastructure to monitor
  • Development speed: Initial features take longer to build
  • You’re building financial systems
  • Audit trails are legally required
  • You have complex, evolving business rules
  • You’re doing microservices and struggling with data consistency
  • You need to answer temporal queries
  • Your application starts hitting audit requirements
  • You need better microservice integration
  • Traditional approaches become too complex
  • You need to debug complex business scenarios
  • Your domain is simple and stable
  • Performance is critical
  • Your team is small and learning
  • You’re building a content site or simple CRUD app

Most event sourcing solutions require you to:

  • Set up complex infrastructure (Kafka, EventStore, etc.)
  • Manage consistency across multiple databases
  • Handle event versioning and migration
  • Build operational tooling for monitoring

DeltaBase removes these barriers:

  • Zero infrastructure: Runs on Cloudflare’s edge
  • Built-in consistency: ACID transactions for events
  • Automatic scaling: Handles traffic spikes transparently
  • Developer-friendly: TypeScript SDK, not configuration files

Event sourcing doesn’t have to be complex. It just needs to solve the right problems for your application.