Event Sourcing & CQRS

The core idea is deceptively simple: instead of storing the current state of something, you store the sequence of things that happened to it. Current state is never saved — it’s always derived by replaying those events from the beginning.

That derivation is called a projection, and it’s just a fold: start with an initial state, apply each event in order, and arrive at where you are now. If you want a different view of the same data, you write a different fold.

Commands and events

Commands are the other half. Before anything gets persisted, a command handler validates the intent against the current projected state and decides whether to accept or reject it. If it’s valid, the handler produces new events — it doesn’t mutate anything directly.

This separation matters: commands can fail, events cannot. Once an event is in the log, it happened. A command like Withdraw 100 might be rejected for insufficient funds; the event Withdrawn 100 is a statement of fact.

CQRS: splitting reads from writes

CQRS (Command Query Responsibility Segregation) builds on this by separating the write and read sides entirely.

Because read models are derived, you can add a new one at any time and populate it by replaying history — no migration of “current state” required.

Process managers

For workflows that span multiple aggregates — “open an account, then fund it, then notify another service” — there are process managers. They listen to events from one aggregate and issue commands to others, coordinating multi-step flows without coupling the aggregates directly. When a step can fail, they carry their own compensation logic.

How Eventium models these

Each concept is a small, first-class value:

Concept In Eventium One-liner
Projection Projection state event a seed + an event handler (a fold)
Command handling CommandHandler state event command err a pure decide + a projection
Workflow ProcessManager state event command a pure react returning effects
Query view ReadModel m event initialize / handle / checkpoint / reset
Storage EventStoreReader / EventStoreWriter records of functions, swappable

Two properties fall out of this design and recur everywhere:

Where to go next