Event Sourcing in Elixir
Plan
II. CQRS / ES with Commanded
IV. What is Elixir?
V. Why is Elixir a good fit?
VI. Demo
I. What are Event Sourcing and CQRS?
III. PoC Presentation
Why Event Sourcing?
Problem: Trace history of Loan Application
Sub-Problem: The best representation of our data
User
Mansa Ops
Mansa Data Scientist
What is Event Sourcing?

What is Event Sourcing?
Transfer +20
Transfer +100
Transfer -40
Transfer +5
time
Current Balance = 85
What is Event Sourcing?
bring the 4th dimension (time) in our data layer
build multiple representations of the data
What is CQRS?
Command Query Responsability Segregation
Write Side
Read Side
Commands
Projections
Events
RequestLoan
LoanApplication (status: 'pending_submission')
LoanRequested
Ex:
II. CQRS / ES with Commanded
CQRS / ES with Commanded
Router
Controller / Consumer
Write Side
Command
Event
Client
HTTP or RabbitMQ
RequestLoan
loan_application_uuid
amount
duration
Aggregate
Event
Event Store
CQRS / ES with Commanded
Router
Aggregate
Controller / Consumer
Write Side
Command
Event
Client
HTTP or RabbitMQ
RequestLoan
loan_application_uuid
amount
duration
LoanRequest
Event
Event Store
LoanRequested
loan_application_uuid
amount
duration
State
CQRS / ES with Commanded
Router
Aggregate
Controller / Consumer
State
Event
Event Store
Write Side
Command
Event
Client
HTTP or RabbitMQ
LoanRequested
loan_application_uuid
amount
duration
LoanRequest
loan_application_uuid
status: 'pending_submission'
RequestLoan
loan_application_uuid
amount
duration
Projector
Projection
Read Store
Read Side
Database transaction
LoanApplication
loan_application_uuid
amount
duration
status: 'pending_submission'
LoanApplicationProjector
CQRS / ES with Commanded
Router
Aggregate
Controller / Consumer
State
Event
Event Store
Write Side
Command
Event
Client
HTTP or RabbitMQ
LoanRequest
loan_application_uuid
status: 'pending_submission'
Projector
Projection
Read Store
Read Side
Database transaction
LoanApplication
loan_application_uuid
amount
duration
status: 'pending_submission'
LoanApplicationProjector
SubmitLoanRequest
loan_application_uuid
CQRS / ES with Commanded
Router
Aggregate
Controller / Consumer
State
Event
Event Store
Write Side
Command
Event
Client
HTTP or RabbitMQ
LoanRequest
loan_application_uuid
status: 'pending_submission'
Projector
Projection
Read Store
Read Side
Database transaction
LoanApplication
loan_application_uuid
amount
duration
status: 'pending_submission'
LoanApplicationProjector
SubmitLoanRequest
loan_application_uuid
LoanRequestSubmitted
loan_application_uuid
CQRS / ES with Commanded
Router
Aggregate
Controller / Consumer
State
Event
Event Store
Write Side
Command
Event
Client
HTTP or RabbitMQ
LoanRequest
loan_application_uuid
status: 'submitted'
Projector
Projection
Read Store
Read Side
Database transaction
LoanApplication
loan_application_uuid
amount
duration
status: 'pending_submission'
LoanApplicationProjector
SubmitLoanRequest
loan_application_uuid
LoanRequestSubmitted
loan_application_uuid
CQRS / ES with Commanded
Router
Aggregate
Controller / Consumer
State
Event
Event Store
Write Side
Command
Event
Client
HTTP or RabbitMQ
LoanRequest
loan_application_uuid
status: 'submitted'
Projector
Projection
Read Store
Read Side
Database transaction
LoanApplication
loan_application_uuid
amount
duration
status: 'submitted'
LoanApplicationProjector
SubmitLoanRequest
loan_application_uuid
LoanRequestSubmitted
loan_application_uuid
CQRS / ES with Commanded
Router
Aggregate
Controller / Consumer
State
Event
Event Store
Write Side
Command
Event
Client
HTTP or RabbitMQ
Projector
Projection
Read Store
Read Side
Database transaction
Event Handler
III. PoC Presentation
Request Loan
Submit Loan Request
Approve Loan
Deny Loan
Sign Contract
Reject Contract
Loan Requested
Loan Request Submitted
Loan Approved
Loan Denied
Contract Signed
Contract Rejected
User
Mansa Admin
User
Commands &
Events
Request Loan
Submit Loan Request
Approve Loan
Deny Loan
Sign Contract
Reject Contract
Loan Requested
Loan Request Submitted
Loan Approved
Loan Denied
Contract Signed
Contract Rejected
Loan Contract
Loan Review
Loan Request
Commands &
Events
& Aggregates
Loan Requested
Loan Request Submitted
Loan Approved
Loan Denied
Contract Signed
Contract Rejected
Loan Contract
Loan Review
Loan Request
loan_application_uuid: uuid
status: 'pending_submission'
loan_application_uuid: uuid
status: 'submitted'
loan_application_uuid: uuid
status: 'approved'
loan_application_uuid: uuid
status: 'denied'
loan_application_uuid: uuid
status: 'signed'
loan_application_uuid: uuid
status: 'rejected'
Commands &
Events
& Aggregates
Loan Requested
Loan Request Submitted
Loan Approved
Loan Denied
Contract Signed
Contract Rejected
Loan Contract
Loan Review
Loan Request
loan_application_uuid: uuid
status: 'pending_submission'
loan_application_uuid: uuid
status: 'submitted'
loan_application_uuid: uuid
status: 'approved'
loan_application_uuid: uuid
status: 'pending_review'
loan_application_uuid: uuid
status: 'denied'
loan_application_uuid: uuid
status: 'pending_signature'
loan_application_uuid: uuid
status: 'signed'
loan_application_uuid: uuid
status: 'rejected'
Loan Review Issued
Loan Contract Issued
Issue Loan Review
Issue Loan Contract
Review Loan Request
Contractualize Approved Loan
Commands &
Events
& Aggregates
& Event Handlers
Loan Requested
Loan Request Submitted
Loan Approved
Loan Denied
Contract Signed
Contract Rejected
Loan Application Projector
INSERT (loan_application_uuid: uuid, amount: 1000, duration: 6, status: 'pending_submission')
Loan Review Issued
Loan Contract Issued
Loan Application
loan_application_uuid (uuid)
amount (integer)
duration (integer)
status: (
'pending_submission'
| 'submitted'
| 'pending_review'
| 'approved'
| 'denied'
| 'pending_signature'
| 'signed'
| 'rejected'
)
Events
& Projectors
& Projections
UPDATE (status: 'submitted')
UPDATE (status: 'signed')
UPDATE (status: 'pending_review')
UPDATE (status: 'approved')
UPDATE (status: 'denied')
UPDATE (status: 'pending_signature')
UPDATE (status: 'rejected')
IV. What is Elixir?
What is Elixir?
based on Erlang/OTP (Open Telecom Platform)

1986


What is Elixir?
Erlang/OTP (Open Telecom Platform)
90% of all internet traffic going through routers and switches controlled by Erlang

Elixir (based on Erlang)





V. Why is Elixir a good fit?
V. Why is Elixir a good fit?
1. Pattern matching
2. OTP: Actor model with Supervision tree
Map & Struct
# Map
%{
first_name: "Hubert",
last_name: "Bonisseur"
}
# Struct
defmodule Person do
defstruct first_name: "", last_name: ""
end
%Person{
first_name: "Hubert",
last_name: "Bonisseur"
}
Modules & Functions
defmodule Maths do
def fibonacci(n) do
fibonacci(n-1) + fibonacci(n-2)
end
endModules & Functions
defmodule Maths do
# Long form
def fibonacci(n) do
fibonacci(n-1) + fibonacci(n-2)
end
# Short form
def fibonacci(n), do: fibonacci(n-1) + fibonacci(n-2)
end1. Pattern matching
defmodule Maths do
def fibonacci(n) do
fibonacci(n-1) + fibonacci(n-2)
end
end1. Pattern matching
defmodule Maths do
def fibonacci(0), do: 0
def fibonacci(1), do: 1
def fibonacci(n) do
fibonacci(n-1) + fibonacci(n-2)
end
end1. Pattern matching
defmodule Maths do
def fibonacci(0), do: 0
def fibonacci(1), do: 1
def fibonacci(n) when is_number(n) and n >= 0 do
fibonacci(n-1) + fibonacci(n-2)
end
endwith Guards
1. Pattern matching
defmodule Maths do
def fibonacci(0), do: 0
def fibonacci(1), do: 1
def fibonacci(n) when is_number(n) and n >= 0 do
fibonacci(n-1) + fibonacci(n-2)
end
def fibonacci(n) when is_number(n) do
raise "Number must be positive"
end
def fibonacci(_n) do
raise "Not a number"
end
endwith Guards
1. Pattern matching
defmodule Aggregates.LoanRequest do
alias Commands.RequestLoan
alias Events.LoanRequested
enduseful for Event Sourcing
1. Pattern matching
defmodule Aggregates.LoanRequest do
alias Commands.RequestLoan
alias Events.LoanRequested
# Command handlers
def execute(state, %RequestLoan{amount: amount}) when amount < 500 do
{:error, :amount_is_too_low}
end
end1. Pattern matching
defmodule Aggregates.LoanRequest do
alias Commands.RequestLoan
alias Events.LoanRequested
# Command handlers
def execute(state, %RequestLoan{amount: amount}) when amount < 500 do
{:error, :amount_is_too_low}
end
def execute(state, %RequestLoan{duration: duration}) when duration < 3 do
{:error, :duration_is_too_low}
end
end1. Pattern matching
defmodule Aggregates.LoanRequest do
alias Commands.RequestLoan
alias Events.LoanRequested
# Command handlers
def execute(state, %RequestLoan{amount: amount}) when amount < 500 do
{:error, :amount_is_too_low}
end
def execute(state, %RequestLoan{duration: duration}) when duration < 3 do
{:error, :duration_is_too_low}
end
def execute(state, %RequestLoan{amount: amount, duration: duration}) do
%LoanRequested{
amount: amount,
duration: duration
}
end
end2. Actor model
Supervision tree
Process
BEAM Virtual Machine
State
Messages
2. Actor model
Supervision tree
Process
"Let it crash"
BEAM Virtual Machine
State
Messages
Supervisor
Workers
2. Actor model
defmodule Aggregates.LoanRequest do
end2. Actor model
defmodule Aggregates.LoanRequest do
alias Aggregates.LoanRequest
alias Commands.{RequestLoan, SubmitLoanRequest}
alias Events.{LoanRequested, LoanRequestSubmitted}
end2. Actor model
defmodule Aggregates.LoanRequest do
alias Aggregates.LoanRequest
alias Commands.{RequestLoan, SubmitLoanRequest}
alias Events.{LoanRequested, LoanRequestSubmitted}
# State
defstruct [
loan_application_uuid: nil,
status: nil
]
end2. Actor model
defmodule Aggregates.LoanRequest do
alias Aggregates.LoanRequest
alias Commands.{RequestLoan, SubmitLoanRequest}
alias Events.{LoanRequested, LoanRequestSubmitted}
# State
defstruct [
loan_application_uuid: nil,
status: nil
]
# Command handlers
def execute(state, %RequestLoan{} = command) do
end
end2. Actor model
defmodule Aggregates.LoanRequest do
alias Aggregates.LoanRequest
alias Commands.{RequestLoan, SubmitLoanRequest}
alias Events.{LoanRequested, LoanRequestSubmitted}
# State
defstruct [
loan_application_uuid: nil,
status: nil
]
# Command handlers
def execute(state, %RequestLoan{} = command) do
%LoanRequested{ ... }
end
end2. Actor model
defmodule Aggregates.LoanRequest do
alias Aggregates.LoanRequest
alias Commands.{RequestLoan, SubmitLoanRequest}
alias Events.{LoanRequested, LoanRequestSubmitted}
# State
defstruct [
loan_application_uuid: nil,
status: nil
]
# Command handlers
def execute(state, %RequestLoan{} = command) do
%LoanRequested{ ... }
end
def execute(state, %SubmitLoanRequest{} = command) do
end
end2. Actor model
defmodule Aggregates.LoanRequest do
alias Aggregates.LoanRequest
alias Commands.{RequestLoan, SubmitLoanRequest}
alias Events.{LoanRequested, LoanRequestSubmitted}
# State
defstruct [
loan_application_uuid: nil,
status: nil
]
# Command handlers
def execute(state, %RequestLoan{} = command) do
%LoanRequested{ ... }
end
def execute(state, %SubmitLoanRequest{} = command) do
%LoanRequestSubmitted{ ... }
end
end2. Actor model
defmodule Aggregates.LoanRequest do
alias Aggregates.LoanRequest
alias Commands.{RequestLoan, SubmitLoanRequest}
alias Events.{LoanRequested, LoanRequestSubmitted}
# State
defstruct [
loan_application_uuid: nil,
status: nil
]
# Command handlers
def execute(state, %RequestLoan{} = command) do
%LoanRequested{ ... }
end
def execute(state, %SubmitLoanRequest{} = command) do
%LoanRequestSubmitted{ ... }
end
# State mutators
def apply(state, %LoanRequested{} = event) do
end
end2. Actor model
defmodule Aggregates.LoanRequest do
alias Aggregates.LoanRequest
alias Commands.{RequestLoan, SubmitLoanRequest}
alias Events.{LoanRequested, LoanRequestSubmitted}
# State
defstruct [
loan_application_uuid: nil,
status: nil
]
# Command handlers
def execute(state, %RequestLoan{} = command) do
%LoanRequested{ ... }
end
def execute(state, %SubmitLoanRequest{} = command) do
%LoanRequestSubmitted{ ... }
end
# State mutators
def apply(state, %LoanRequested{} = event) do
%LoanRequest{loan_application_uuid: event.loan_application_uuid, status: "pending_submission"}
end
end2. Actor model
defmodule Aggregates.LoanRequest do
alias Aggregates.LoanRequest
alias Commands.{RequestLoan, SubmitLoanRequest}
alias Events.{LoanRequested, LoanRequestSubmitted}
# State
defstruct [
loan_application_uuid: nil,
status: nil
]
# Command handlers
def execute(state, %RequestLoan{} = command) do
%LoanRequested{ ... }
end
def execute(state, %SubmitLoanRequest{} = command) do
%LoanRequestSubmitted{ ... }
end
# State mutators
def apply(state, %LoanRequested{} = event) do
%LoanRequest{loan_application_uuid: event.loan_application_uuid, status: "pending_submission"}
end
def apply(state, %LoanRequestSubmitted{} = event) do
end
end2. Actor model
defmodule Aggregates.LoanRequest do
alias Aggregates.LoanRequest
alias Commands.{RequestLoan, SubmitLoanRequest}
alias Events.{LoanRequested, LoanRequestSubmitted}
# State
defstruct [
loan_application_uuid: nil,
status: nil
]
# Command handlers
def execute(state, %RequestLoan{} = command) do
%LoanRequested{ ... }
end
def execute(state, %SubmitLoanRequest{} = command) do
%LoanRequestSubmitted{ ... }
end
# State mutators
def apply(state, %LoanRequested{} = event) do
%LoanRequest{loan_application_uuid: event.loan_application_uuid, status: "pending_submission"}
end
def apply(state, %LoanRequestSubmitted{} = event) do
%LoanRequest{state | status: "submitted"}
end
endVI. Demo
Mansa - PoC Loans CQRS/ES
By Basile NOUVELLET
Mansa - PoC Loans CQRS/ES
- 31