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
Problem: Trace history of Loan Application
Sub-Problem: The best representation of our data
User
Mansa Ops
Mansa Data Scientist
Transfer +20
Transfer +100
Transfer -40
Transfer +5
time
Current Balance = 85
bring the 4th dimension (time) in our data layer
build multiple representations of the data
Command Query Responsability Segregation
Write Side
Read Side
Commands
Projections
Events
RequestLoan
LoanApplication (status: 'pending_submission')
LoanRequested
Ex:
Router
Controller / Consumer
Write Side
Command
Event
Client
HTTP or RabbitMQ
RequestLoan
loan_application_uuid
amount
duration
Aggregate
Event
Event Store
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
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
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
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
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
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
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
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
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
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'
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
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'
)
UPDATE (status: 'submitted')
UPDATE (status: 'signed')
UPDATE (status: 'pending_review')
UPDATE (status: 'approved')
UPDATE (status: 'denied')
UPDATE (status: 'pending_signature')
UPDATE (status: 'rejected')
based on Erlang/OTP (Open Telecom Platform)
1986
Erlang/OTP (Open Telecom Platform)
90% of all internet traffic going through routers and switches controlled by Erlang
Elixir (based on Erlang)
1. Pattern matching
2. OTP: Actor model with Supervision tree
# Map
%{
first_name: "Hubert",
last_name: "Bonisseur"
}
# Struct
defmodule Person do
defstruct first_name: "", last_name: ""
end
%Person{
first_name: "Hubert",
last_name: "Bonisseur"
}
defmodule Maths do
def fibonacci(n) do
fibonacci(n-1) + fibonacci(n-2)
end
enddefmodule 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)
enddefmodule Maths do
def fibonacci(n) do
fibonacci(n-1) + fibonacci(n-2)
end
enddefmodule Maths do
def fibonacci(0), do: 0
def fibonacci(1), do: 1
def fibonacci(n) do
fibonacci(n-1) + fibonacci(n-2)
end
enddefmodule 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
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
defmodule Aggregates.LoanRequest do
alias Commands.RequestLoan
alias Events.LoanRequested
enduseful for Event Sourcing
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
enddefmodule 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
enddefmodule 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
endSupervision tree
Process
BEAM Virtual Machine
State
Messages
Supervision tree
Process
"Let it crash"
BEAM Virtual Machine
State
Messages
Supervisor
Workers
defmodule Aggregates.LoanRequest do
enddefmodule Aggregates.LoanRequest do
alias Aggregates.LoanRequest
alias Commands.{RequestLoan, SubmitLoanRequest}
alias Events.{LoanRequested, LoanRequestSubmitted}
enddefmodule Aggregates.LoanRequest do
alias Aggregates.LoanRequest
alias Commands.{RequestLoan, SubmitLoanRequest}
alias Events.{LoanRequested, LoanRequestSubmitted}
# State
defstruct [
loan_application_uuid: nil,
status: nil
]
enddefmodule 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
enddefmodule 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
enddefmodule 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
enddefmodule 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
enddefmodule 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
enddefmodule 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
enddefmodule 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
enddefmodule 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
end