tool

Event Store

An append-only store of domain events organised into per-entity streams, serving as the system of record rather than as a log beside it.

event-sourcingappend-onlystreams

The operations an event store must support are narrow but specific: append events to a stream with an expected version (which is how optimistic concurrency is enforced — the append fails if someone else wrote first), read a stream forwards from a position, and subscribe to new events across streams.

That expected-version check is the part people miss when building one on a general-purpose database. Without it, two concurrent commands can both read state, both decide, and both append — producing a stream that violates the invariant the aggregate was protecting.

Stream design is the modelling decision. One stream per aggregate instance — per order, per account — is the norm, because that is the consistency boundary and the unit you load. Streams that are too coarse become hot and unbounded; too fine and an aggregate's invariants span streams, which is the same problem as a badly-drawn service boundary.

Purpose-built options exist (EventStoreDB, Axon, Marten over Postgres), and a plain relational table with (stream_id, version, event_type, payload) and a unique constraint on (stream_id, version) works well for moderate volumes — the unique constraint is the concurrency check.