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"}
]
end
Update and compile:
mix do deps.get, deps.compile, compile
Clean useless stuff
Delete the following files:
my_first_cqrses/lib/my_first_cqrses.ex
my_first_cqrses/test/my_first_cqrses_test.exs
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"}
]
end
and configure the connection:
config :seven, Seven.Data.Persistence,
database: "my_first_cqrses",
hostname: "127.0.0.1",
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"}
]
end
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 [
:map,
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]
}
@register_user_command
|> Seven.Otters.Command.create(cmd)
|> validate(@register_user_validation)
end
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})]}
end
@spec handle_event(Map.t(), any) :: any
defp handle_event(%Seven.Otters.Event{type: @user_registered_event} = event, state) do
%{
state
| user: event.payload.v1.user,
password: event.payload.v1.password
}
end
end
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 =
%Seven.CommandRequest{
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}
end
end
Run the test:
mix test
Start your application
Start your new application with:
mix run --no-halt
# or
iex -S mix
Congratulation!
Good job! You have just create your first CQRS/ES application in Elixir.
Learn more
- Official website: https://www.sevenotters.org/
- Docs: https://hexdocs.pm/seven
- Source: https://github.com/sevenotters
Feedback, requests, help, anythings else
For now any communication with the Seven Otters project team is by pull requests or at seven.otters.project@gmail.com.
If you like the project, any active help (in any form) is absolutly welcome.