Skip to content
Event sourced state machines
Branch: develop
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.
.circleci Release/1.13.0 (#54) Mar 6, 2019
build.python-library/docker-base Generating build files through globality-build Dec 18, 2018
.bumpversion.cfg Bump Apr 22, 2019
Dockerfile.template Generating build files through globality-build Dec 18, 2018
LICENSE Release/1.13.0 (#54) Mar 6, 2019
setup.cfg Release/1.13.0 (#54) Mar 6, 2019


Event-sourced state machines using microcosm.

Manages state changes as an immutable event log using:

By Example

Imagine a task list with the following rules:

  • A task is created with a text description.

  • After a task is created, it can be assigned to an assignee and scheduled for a deadline. These operations can happen in any order.

  • After a task is both assigned and scheduled, it may be started.

  • After a task is started, it may be reassigned or rescheduled any number of times.

  • After a task is started, it may either be canceled or completed. Both of these states are terminal.

  • Before a task is canceled or completed, it maybe revised at any time. A revised task reverts back to the initial state.

These actions collectively define a state machine, where each transition is triggered by a new event:

          => ASSIGNED => SCHEDULED =>             => CANCELED
        /                             \         /
CREATED                                 STARTED
        \                             /         \
          => SCHEDULED => ASSIGNED =>             => COMPLETED

(For simplicity, the REASSIGNED, RESCHEDULED, and REVISED events are not shown.)

Defining Tasks

Every task will be defined in a task table, which needs only the default, auto-generated primary key and timestamp fields:

class Task(UnixTimestampEntityMixin, Model):
    __tablename__ = "task"

class TaskStore(Store):

    def __init__(self, graph):
        super(TaskStore, self).__init__(graph, Task)

Defining Task Events

Every action that changes states will be defined in a task_event table, which has:

  • a foreign key reference back to the "container" task table
  • a reference to an event type enumeration (see below)
  • zero or more additional columns

For example:

class TaskEvent(UnixTimestampEntityMixin):
    __tablename__ = "task_event"
    __eventtype__ = TaskEventType
    __container__ = Task

    assignee = Column(String)
    deadline = Column(DateTime)

class TaskEventStore(EventStore):

    def __init__(self, graph):
        super(TaskEventStore, self).__init__(graph, TaskEvent)

Defining Task Event Types

It remains to define the legal events and their transitions using an enumeration. This is done as follows:

  • Each enumerated value is identified using its (left hand side) name.
  • Each enumerated value is configured using its (right hand side) meta data.
  • This meta data includes:
    • "following conditions" for legal state transitions.
    • a flag controlling whether state accumulates through this transition
    • a flag controlling whether the state machine is restarted (with a new version) after this state
    • a list of zero or more required additional columns for this state


class TaskEventType(EventType):
    CREATED = event_info()
    ASSIGNED = event_info(
        follows=all_of("CREATED", but_not("ASSIGNED")),
    SCHEDULED = event_info(
        follows=all_of("CREATED", but_not("SCHEDULED")),
    STARTED = event_info(
        follows=all_of("ASSIGNED", "SCHEDULED"),
    REASSIGNED = event_info(
    RESCHEDULED = event_info(
    REVISED = event_info(
        follows=any_of("CREATED", "STARTED"),
    CANCELED = event_info(
    COMPLETED = event_info(

Running tests

First, create role and DB:

createuser microcosm_eventsource
createdb -O microcosm_eventsource microcosm_eventsource_test_db

Second, install nose:

pip install nose pyhamcrest

Then, run nosetests

You can’t perform that action at this time.