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