Skip to content

Instantly share code, notes, and snippets.

@eulerfx
Last active July 15, 2024 13:01
Show Gist options
  • Save eulerfx/4ac420a14422ac960222 to your computer and use it in GitHub Desktop.
Save eulerfx/4ac420a14422ac960222 to your computer and use it in GitHub Desktop.
The relationship between state machines and event sourcing

A state machine is defined as follows:

  • Input - a set of inputs
  • Output - a set of outputs
  • State - a set of states
  • S0 ∈ S - an initial state
  • T : Input * State -> Output * State - a transition function

If you model your services (aggregates, projections, process managers, sagas, whatever) as state machines, one issue to address is management of State. There must be a mechanism to provide State to the state machine, and to persist resulting State for subsequent retrieval. One way to address this is by storing State is a key-value store. Another way is to use a SQL database. Yet another way is event sourcing. The benefit of even sourcing is that you never need to store State itself. Instead, you rely on the Output of a service to reconstitute state. In order to do that, the state machine transition function needs to be factored into two functions as follows:

exec  : Input * State -> Output
apply : Output * State -> State

These two functions can be combined to yield the original transition function with the added benefit that the apply function can be used to reconstitute state based on past outputs. This can be done as follows:

state = fold apply S0 outputs

Where fold is a left fold and outputs is a set of outputs retrieved from an event store (such as @GetEventStore).

In order for this to be correct, the apply functions needs to be deterministic. This is where the event semantic becomes helpful.

Alternatively, the transition function can be factored a slightly different way to support command sourcing as follows:

exec  : Input * State -> Output
apply : Input * State -> State

These functions can also be combined to yield the state machine transition function. The difference from event sourcing, is that we rely on past inputs rather than past outputs to reconstitute state. Note also that the apply functions has to be deterministic. Also, with command sourcing, the Inputs must be stored in a durable log (such as @GetEventStore).

An example in F# here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment