A state machine is defined as follows:
Input
- a set of inputsOutput
- a set of outputsState
- a set of statesS0 ∈ S
- an initial stateT : 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 Input
s must be stored in a durable log (such as @GetEventStore).
An example in F# here.