Event Sourcing at Commerce Scale: Lessons Learned
The question we get more than almost any other is: 'Can you show me exactly what happened to Order #84721 — every state change, every amount, every actor, with timestamps?' The answer is always yes. That answer is an architectural decision we made very early, and it has saved us more than once.
The question we get more than almost any other from enterprise merchants is: 'Can you show me exactly what happened to Order #84721 — every state change, every amount, every actor, from placement to delivery, with timestamps?' The answer is always yes, and we can show it in under 200ms. That answer is not the result of good logging practices. It is an architectural decision we made before we wrote our first order handler.
What Event Sourcing Means in Commerce
Most commerce databases store current state. The orders table has one row per order, and that row is updated as the order moves through its lifecycle. When the order ships, status is updated. When a partial refund is issued, amount_paid is updated. The history — who changed what, when, and why — is gone unless you separately built an audit log, which is almost always an afterthought and almost always incomplete.
In an event-sourced system, the orders table does not store current state. It stores events. OrderPlaced. PaymentCaptured. ItemShipped. PartialRefundIssued. DeliveryConfirmed. The current state of an order is derived by replaying its events. The event log is the source of truth. The current state is a read-model, a cache of what the events say right now.
The Idempotency Problem
The hardest problem in event sourcing at scale is not storage. It is idempotency. Payment webhooks arrive twice. Network timeouts cause clients to retry. A Razorpay webhook for payment.captured might arrive three times in 30 seconds if the first two responses time out. If you process all three, you get three PaymentCaptured events, and your order total is wrong.
Our solution is an event fingerprint — a deterministic identifier derived from the event source (Razorpay payment ID), the event type, and the tenant. Before appending an event, we attempt to insert the fingerprint into a deduplication table. If the insert fails (because the fingerprint already exists), we return success to the caller without appending the event. The Razorpay webhook gets a 200 response. The event does not get written twice. The order total is correct.
The Projection Architecture
Read models (projections) are built by a background worker that consumes events in sequence. Our orders_current projection is a materialized view of the current state of every order, rebuilt from events on a rolling basis. When a customer service agent opens Order #84721, they see the current state from the projection. When they click 'show history,' they see the raw event log. Both are fast, because both are pre-computed.
Lessons from Commerce-Scale Event Volumes
At multi-tenant commerce scale, event volumes are higher than most engineers expect. A busy sale day for a single tenant might generate 50,000 events. Across all tenants — inventory adjustments, loyalty point accruals, catalog price changes, POS transactions — the platform generates millions of events per day. We learned three things the hard way:
- Partition events by tenant early. A single monolithic event log becomes a performance bottleneck before you expect it to. Partitioning by tenant_id keeps each tenant's event stream manageable and enables per-tenant replay without touching other tenants' data.
- Version your event schemas from day one. An
OrderPlaced_v1event may have different fields fromOrderPlaced_v2. If you do not version events, you cannot evolve the schema without breaking replay. Version from the first event you write. - Test replay regularly, not just in emergencies. We run a weekly replay test against a staging database. This ensures that replaying the event log from scratch produces the same state as the live database. If they diverge, we know before a production incident forces us to find out.
The Business Value for Indian Commerce
GST compliance in India requires a complete audit trail of every commercial transaction. Dispute resolution with payment gateways (Razorpay, Cashfree) requires showing the exact sequence of payment events. Customer service for a karupatti sweet brand needs to answer 'why was this order cancelled' for an order from six months ago. Event sourcing makes all of these trivially answerable — not because we built special audit tools, but because the complete history is the data model.
The event log is not a feature. It is the foundation.
Subscribe to the QuantumOS Dispatch — weekly insights for commerce operators who want to compound their advantages.
QuantumOS Dispatch
Weekly insights for commerce operators
100 competitive moats, real operator stories, platform updates. No fluff. Every Tuesday.
No spam. Unsubscribe any time. 60k+ readers.