CQRS & Event Sourcing

Dariusz Pawlukiewicz

IT Academic Day 20.10.2016

Where can you find me?

Blog: Forever F[r]ame

Podcast: DevReview

GitHub

@d_pawlukiewicz

Forever Frame

dpawlukiewicz

foreverframe.pl

devreview.pl

github.com/GooRiOn

Asynchronous execution

async Task<string> GetTextAsync()
{
    Task<string> task = DoSomethingAsync();

    DoSomethingSync();

    string taskResult = await task;

    return taskResult;
}

async Task<string> DoSomethingAsync()
    => await Task.Run(async () =>
    {
        await Task.Delay(5000);
        return "I ran async";
    });     

void DoSomethingSync()
{
    for (var i = 0; i < 100; ++i)
        Console.WriteLine(i);
}

CQS

Command Query Separation (Bertrand Mayer, 1986)

What is CQS?

Methods

Commands

Queries

  • void return type
  • MUTATE domain's state
  • non void return type
  • DON'T mutate domain's state

"Asking a question should not change the answer"

~ Martin Fowler

CQS example

Command

Query

Idempotent

Idenpotency

f(f(x)) = f(x)

REST example?

PUT, GET, DELETE

CQS Exception

Stack.Pop - ()

 

Removes AND returns the object at the top of the Stack.

CQRS

Command Query Responsibility Segregation

The born of CQRS

CQS +

+

= CQRS

What is CQRS?

Methods

Commands

Queries

  • void return type
  • DON'T mutate domain's state
  • non void return type
  • MUTATE domain's state

Objects

CQRS is NOT
an architecture!

My approach to CQRS

Command Bus

Command Handler

DAL

Domain object

Write DB

Event Bus

Event Handler

Read DB

Read DB abstraction

command

read model

query

ack

Command

Object which represents user's intention

DTO vs. Command

DTO

Data centric

Command

Behavior centric

Command Bus

Command Handler

Domain object

Write DB

Event Bus

Event Handler

Read DB

Read DB abstraction

command

read model

query

ack

DAL

Command Bus

  • Queue all incomming commands (RabbitMQ)
  • Sends async command to proper Command Handler

Command Bus

Command Handler

Domain object

Write DB

Event Bus

Event Handler

Read DB

Read DB abstraction

command

read model

query

ack

DAL

Command Handler

  • Validates received command
  • Do some "magic" with domain objects

Command Bus

Command Handler

Domain object

Write DB

Event Bus

Event Handler

Read DB

Read DB abstraction

command

read model

query

ack

DAL

Domain object

  • Contains business complexity of our system
  • Produces domain events 

Command Bus

Command Handler

Domain object

Write DB

Event Bus

Event Handler

Read DB

Read DB abstraction

command

read model

query

ack

DAL

Event Bus

  • Queue all incomming events (RabbitMQ)
  • Sends async event to proper Event Handler

FIRE AND FORGET

Command Bus

Command Handler

Domain object

Write DB

Event Bus

Event Handler

Read DB

Read DB abstraction

command

read model

query

ack

DAL

Event Handler

  • Does NOT validate an event
  • Saves entity into Read DB

Command Bus

Command Handler

Domain object

Write DB

Event Bus

Event Handler

Read DB

Read DB abstraction

command

read model

query

ack

DAL

Command Bus

Command Handler

Domain object

Write DB

Write side

  • Contains business logic

  • Requires more skill to implement

  • DB can be optimized for writing 

DAL

Event Bus

Event Handler

Read DB

Read DB abstraction

Read side

  • NO business logic

  • Way easier to implement

  • DB can be optimized for read

Event Sourcing

"Current state" database

Id Name Quantity Price IsDeleted
1 Book1 23 34 0
2 Book2 12 20 1

Lost Update...

Lost update example

Get Book1 data

Set quantity to 3

Save changes

Get Book1 data

Set quantity to 22

Save changes

t

What if...

Keeping events instead of current state

Id Name Price
1 Book1 12

Event

Sounds odd?

Event sourced example

Get Book1 data

Create new BookSold event

Save changes

Get Book1 data

Save changes

t

Create new BookSold event

Id BookId Data Type DateTime
1 1 {Name: 'Book1',...} BookCreated 2016-02-12 12:00
2 1 {Quantity: 1} BookSold 2016-04-23 09:34
3 2 {Name: 'Book2',...} BookCreated 2016-06-12 12:44
4 5 {Name: 'Book5'} BookRenamed 2016-06-15 23:44
... ... ... ... ...
678 1 {Quantity: 12} BookSold 2016-12-12 20:49

Event Store

Reconstructing domain object

Id BookId Data Type DateTime
1 1 {Name: 'Book1',...} BookCreated 2016-02-12 12:00
2 1 {Quantity: 1} BookSold 2016-04-23 09:34
3 2 {Name: 'Book2',...} BookCreated 2016-06-12 12:44
4 5 {Name: 'Book5'} BookRenamed 2016-06-15 23:44
... ... ... ... ...
678 1 {Quantity: 12} BookSold 2016-12-12 20:49

SELECT Data, Type FROM BookEvents 

         WHERE BookId = 1

               ORDER BY DateTime

Reconstructing domain object

BookId Name Quantity Price IsDeleted
1 Book1 23 37 0

BookCreated

{

  Name: 'Book1',

  Quantity: 23,

  Price: 37

}

BookSold

{  

  Quantity: 20  

}

BookSold

{  

  Quantity: 1  

}

3

2

Time traveling?

Time traveling with ES

Id BookId Data Type DateTime
1 1 {Name: 'Book1',...} BookCreated 2016-02-12 12:00
2 1 {Quantity: 1} BookSold 2016-04-23 09:34
3 2 {Name: 'Book2',...} BookCreated 2016-06-12 12:44
4 5 {Name: 'Book5'} BookRenamed 2016-06-15 23:44
... ... ... ... ...
678 1 {Quantity: 12} BookSold 2016-12-12 20:49

SELECT Data, Type FROM BookEvents 

         WHERE BookId = 1

               ORDER BY DateTime

AND DateTime < '2016-05-12'

Problem

...

Event

#1

Event

#2

Event #1002

Event #1003

Snapshots

...

Event

#998

Event

#999

Snapshot

Event #1000

STATISTICS...

Problem with statistics and ES

...

What we need is database optimized for reading...

CQRS

Command Bus

Command Handler

DAL

Domain object

Write DB

Event Bus

Event Handler

Read DB

Read DB abstraction

command

read model

query

ack

Event Store

Event Store

abstraction

CQRS without ES

ES without CQRS

Why CQRS/ES combo is so cool?

  • Asymmetric scalability
  • Work division
  • Independent sides optimizations (technology / data structures)
  • Microservices / actor model?
  • Time traveling
  • Non-lame audit

Questions?

@d_pawlukiewicz

foreverframe.pl

CQRS & Event Sourcing

By goorion

CQRS & Event Sourcing

  • 130