CQRS/ES in a nutshell

CAVEAT: This post only reflects my current understanding on the topic, are mostly notes for myself and may be fundamentally wrong.

CQRS = Command Query Responsibility Segregation
ES = Event Sourcing

Read from PROJECTIONS,
write to the EVENT STORE.

execute( current_state, COMMAND(payload)) -> error | event(s)

current_state is ONLY USED to determine the validity of the command

apply(current_state, EVENT(payload)) -> new_mutated_state

It MUTATES the current state according to the received event and the result saved in the projection(s).

(Would exaggerate the above paragraph more if I could. Probably using something like this.)

Oversimplified sketch below is pretty faint, but probably inaccurate anyway (at least for now). Posts will follow how CQRS/ES got implemented on Firebase’s Realtime Database and then comparing that with Ben Smith’s commanded and eventstore Elixir libraries. (See also his slides onBuilding CQRS/ES web applications in Elixir using Phoenix.)

Bernardo Amorim’s Code Beam SF 2018 talk, “CQRS and Event Sourcing “ helped things put into place. (Even though I remember seeing the same information in other places. Probably my mind needed to be at the right place.) From that talk:

new_state = apply(previous_state, new_event)
current_state = reduce(all_events, initial_state, `apply`)

State in the captions can refer to

  • the state of the entire application or
  • the state of an individual aggregate instance.

(For example, applying all events from the very beginning would yield the current state. On the other hand if one is interested in a specific entity, e.g. an account’s current balance, only those relevant events will be replayed.)

The term “aggregate” comes from Domain Driven Design (just google it and/or see Ben Smith’s resources on the topic). It refers to the entity that is important for your domain and whose state is crucial for your application (therefore keeping tabs on it is a good idea). The usual example is a bank account for a financial application.

Event store implementations (e.g., Greg Young’s EventStore or Ben Smith’s eventstore) create one stream for each aggregate instance (each with a unique stream ID) and save all events in one stream that pertains that particular aggregate instance. (And provide means to get a list of ALL events in chronological order, irrespective of streams. This is basically a virtual projection.) With an (probably bad) example:

From a Stackoverflow question:

In Event Sourcing, you store all individual Domain Events that have happened for one Aggregate instance, known as Event Stream.

Commands versus Events

(Paraphrasing from the cqrs.nu’s FAQ and Ben Smith’s guide. )

An EVENT represents something that took place in the domain. They are always named with a past-participle verb, such as OrderConfirmed. The reason for the past tense is that it an EVENT is a response to a COMMAND, that has been successfully evaluated (i.e. no errors thrown) against the current state of the system and now it is in the past, it just needs to be applyed (i.e.
(1) mutate the current state,
(2) save the new state into a projection).

A COMMAND is a request to make changes in the domain. They are named with a verb in the imperative mood, because it is not a statement of fact; it's only a request executed against the current state, and thus may be refused (e.g., throw an exception) or return the EVENT(S) otherwise.

current_state below is usually a stream_ID or an aggregate object.

execute(current_state, COMMAND(payload)) -> error | event(s)

COMMANDs are executed, via a function usually with the same name. It is used to enforce the business rules of the domain and resolve conflicts and uses the current state (or previous) to determine whether command can be executed.

This function returns

  • either an error (thus refusing a request)
  • or an event (or multiple events)
    and also saves them in the event store

apply(current_state, EVENT(payload)) -> new_mutated_state

EVENTs are applyed, mutating the state of an in memory object (or process in Erlang/Elixir) and updates the PROJECTIONs.

It mutates the current state, that would be saved into the read projection. For example, updating a row in a SQL table.

Projections

A PROJECTION is the persisted state on an in-memory aggregate object (if there’s any). Reads are usually made from projections, that can be any kind of representations of the events: graphs, SQL tables, NOSQL documets, CSV files etc.

What am I forgetting?…

--

--