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 the resulting
State for subsequent retrieval. One way to address this is by storing
State in a key-value store. Another way is to use a SQL database. Yet another way is event sourcing. The benefit of event 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
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 function 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 function has to be deterministic. Also, with command sourcing, the complete
Input must be stored in a durable log (such as @GetEventStore).
An example in F# here.