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:
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 beapply
ed (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
execute
d 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 execute
d, 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 apply
ed, 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?…