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





  
  
  
  
  
  
  
  
end

Modules & 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)



  
  
  
  
end

1. Pattern matching

defmodule Maths do

  def fibonacci(n) do
    fibonacci(n-1) + fibonacci(n-2)
  end





  
  
  
  
  
  
  
  
end

1. 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
  
  
  
  
  
  
  
  
  
end

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
  
  
  
  
  
  
  
  
  
end

with 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
  
end

with Guards

1. Pattern matching

defmodule Aggregates.LoanRequest do
  alias Commands.RequestLoan
  alias Events.LoanRequested
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  

end

useful 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











end

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
  
  def execute(state, %RequestLoan{duration: duration}) when duration < 3 do
    {:error, :duration_is_too_low}
  end







end

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
  
  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
end

2. 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


 
 
 
 
 
 























end

2. Actor model

defmodule Aggregates.LoanRequest do
  alias Aggregates.LoanRequest
  
  alias Commands.{RequestLoan, SubmitLoanRequest}
  alias Events.{LoanRequested, LoanRequestSubmitted}
 
 
 
 























end

2. 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
  ]
 



















end

2. 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














end

2. 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














end

2. 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










end

2. 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










end

2. 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
  
  
  
  
end

2. 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
  
  
  
  
end

2. 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  
end

2. 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  
end

VI. Demo

Made with Slides.com