Skip to content
an event store + cqrs
Branch: master
Clone or download
Type Name Latest commit message Commit time
Failed to load latest commit information.
config ecto 3 and deps Mar 7, 2019
lib Removed unused dependency Jul 11, 2019
test ecto 3 and deps Mar 7, 2019
.formatter.exs sensible specs Apr 26, 2019
.gitignore the road to 0.1 Apr 4, 2018
.travis.yml travis says we can't have nice things Sep 14, 2018
LICENSE the road to 0.1 Apr 4, 2018 README revelation: remove redundancy Jun 25, 2018
coveralls.json drop pid from new Jun 21, 2018
dialyzer.ignore-warnings mix task for generating migrations Apr 12, 2018
mix.exs deploy fix for migration task Jul 11, 2019
mix.lock prepare for ecto 3 release Apr 21, 2019


Maestro is an event sourcing library. It is inspired by CQRS and re-uses terminology where appropriate. The divergence from being a CQRS framework is intentional as Maestro focuses on processing commands in a consistent manner and replaying events in a consistent order.

Currently, the only storage adapter suited to a multi-node environment is the Maestro.Store.Postgres adapter. The Maestro.Store.InMemory adapter exists for testing purposes only.


Hex Build Status Coverage

Documentation is available here.


def deps do
  [{:maestro, "~> 0.2"}]

Database Configuration

Maestro is intended to be used alongside an existing database/ecto repo.

config :maestro,
  storage_adapter: Maestro.Store.Postgres,
  repo: MyApp.Repo

To generate the migrations for the snapshot and event logs, do:

mix maestro.create.event_store_migration


There are three behaviours that make the command/event lifecycle flow: Maestro.Aggregate.CommandHandler, Maestro.Aggregate.EventHandler, and Maestro.Aggregate.ProjectionHandler. Modules implementing the command and event handler behaviours are looked up via a configurable :command_prefix and :event_prefix respectively. Projections are reserved for maintaining other models/representations within the event's transaction.

defmodule MyApp.Aggregate do
  use Maestro.Aggregate.Root,
    command_prefix: MyApp.Aggregate.Commands,
    event_prefix: MyApp.Aggregate.Events

  def initial_state, do: %{"value" => 0}

  def prepare_snapshot(state), do: state

  def use_snapshot(_curr, %Maestro.Types.Snapshot{body: state}), do: state

defmodule MyApp.Aggregate.Commands.IncrementCounter do

  @behaviour Maestro.Aggregate.CommandHandler

  alias Maestro.Types.Event

  def eval(aggregate, _command) do
        type: "counter_incremented",
        body: %{}

defmodule MyApp.Aggregate.Events.CounterIncremented do

  @behaviour Maestro.Aggregate.EventHandler

  def apply(state, _event), do: Map.update!(state, "value", &(&1 + 1))
iex(1)> {:ok, id} =
iex(2)> :ok = MyApp.Aggregate.evaluate(%Maestro.Types.Command{aggregate_id: id, type: "increment_counter", data: %{}})
iex(3)> {:ok, %{"value" => 1}} = MyApp.Aggregate.get(id)
You can’t perform that action at this time.