Getting started

How to start with The Seven Otters to create your first CQRS/ES application. The aim of this documentation is to introduce the Seven Otters to developers who want to become familiar with the project.

Create and prepare a new project

Create a new project:

mix new my_first_cqrses --sup

Add :seven to project dependencies:

defp deps do
  {:seven, "~> 0.1"}

Update and compile:

mix do deps.get, deps.compile, compile

Clean useless stuff

Delete the following files:


Configure application

Add the following sections to my_first_cqrses/config/config.exs:

config :seven, Seven.Entities,
  entity_app: :my_first_cqrses

config :logger, :console,
  format: "$date-$time [$level] $message\n",
  level: :info

The first section indicates in which application all entities (aggregates, projections, etc.) are defined.

By default Seven Otters uses in memory (and volatile!) event store: events remain in memory and they are lost ending application. To use Postgres as permanet persistence, add to your configuration:

config :seven,
  persistence: SevenottersPostgres.Storage

add sevenotters_postgres to project dependencies:

defp deps do
  {:sevenotters_postgres, "~> 0.1"}

and configure the connection:

config :seven, Seven.Data.Persistence,
  database: "my_first_cqrses",
  hostname: "",
  port: 27_017

To use Elasticsearch, add to your configuration:

config :seven,
  persistence: SevenottersElasticsearch.Storage

add sevenotters_elasticsearch to project dependencies:

defp deps do
  {:sevenotters_elasticsearch, "~> 0.1"}

and configure the connection:

config :seven, Seven.Data.Persistence,
  url: "http://localhost",
  port: 9_200

config :elastix,
  json_options: [keys: :atoms],
  httpoison_options: [hackney: [pool: :elastix_pool]]

Create your first aggregate and add a command

Create a new folder my_first_cqrses/lib/aggregate and create a new file my_first_cqrses/lib/aggregate/user.ex.

Substitute the content of file my_first_cqrses/lib/aggregate/user.ex with the following code:

defmodule MyFirstCqrses.Aggregate.User do
  use Seven.Otters.Aggregate, aggregate_field: :user

  defstruct user: nil,
            password: nil

  @register_user_command "RegisterUser"
  @register_user_validation [
    fields: [
      user: [:string],
      password: [:string, pattern: ~r/.{8,}/]

  @user_registered_event "UserRegistered"

  @moduledoc """
    User aggregate.
    Responds to commands:
    - #{@register_user_command}

  defp init_state, do: %__MODULE__{}

  @spec route(String.t(), any) :: {:routed, Map.y(), atom} | {:invalid, Map.t()}
  def route(@register_user_command, params) do
    cmd = %{
      user: params[:user],
      password: params[:password]

    |> Seven.Otters.Command.create(cmd)
    |> validate(@register_user_validation)

  def route(_command, _params), do: :not_routed

  defp pre_handle_command(_command, _state), do: :ok

  @spec handle_command(Map.t(), any) :: {:managed, List.t()}
  defp handle_command(%Seven.Otters.Command{type: @register_user_command} = command, state) do
    event = %{
      user: command.payload.user,
      password: command.payload.password

    {:managed, [create_event(@user_registered_event, %{v1: event})]}

  @spec handle_event(Map.t(), any) :: any
  defp handle_event(%Seven.Otters.Event{type: @user_registered_event} = event, state) do
      | user: event.payload.v1.user,
        password: event.payload.v1.password


Test the command

Create a new test file my_first_cqrses/test/user_test.exs.

Substitute the content of this file with the following code:

defmodule UserTest do
  use ExUnit.Case

  test "register a new user" do
    Seven.EventStore.EventStore.subscribe("UserRegistered", self())

    request_id = Seven.Data.Persistence.new_id

    result =
        id: request_id,
        command: "RegisterUser",
        sender: __MODULE__,
        params: %{user: "Paul User", password: "my_difficult_password"}
      |> Seven.CommandBus.send_command_request()

    refute result == :not_managed, "Command is not managed by anyone"

    assert_receive %Seven.Otters.Event{type: "UserRegistered", request_id: ^request_id, correlation_module: MyFirstCqrses.Aggregate.User}

Run the test:

mix test

Start your application

Start your new application with:

mix run --no-halt
# or
iex -S mix 


Good job! You have just create your first CQRS/ES application in Elixir.

Learn more

Feedback, requests, help, anythings else

For now any communication with the Seven Otters project team is by pull requests or at

If you like the project, any active help (in any form) is absolutly welcome.