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.
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.