cqrs & event sourcing in the wild (scotlandphp 2016)
TRANSCRIPT
![Page 1: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/1.jpg)
CQRS & EVENT SOURCING IN THE WILD
Michiel Rook - @michieltcs
![Page 2: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/2.jpg)
➤ Java, PHP & Scala developer
➤ Consultant, trainer, speaker
➤ Dutch Web Alliance
➤ make.io
➤ Maintainer of Phing
➤ @michieltcs
![Page 3: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/3.jpg)
YOU
![Page 4: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/4.jpg)
RAISE YOUR HAND
IF YOU HAVE
![Page 5: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/5.jpg)
heard about CQRS / Event Sourcing
RAISE YOUR HAND
IF YOU HAVE
![Page 6: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/6.jpg)
heard about CQRS / Event Sourcing
followed a tutorial, built a hobby project
RAISE YOUR HAND
IF YOU HAVE
![Page 7: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/7.jpg)
heard about CQRS / Event Sourcing
followed a tutorial, built a hobby project
used it in production
RAISE YOUR HAND
IF YOU HAVE
![Page 8: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/8.jpg)
TOPICS
➤ Quick recap
➤ Replays and rebuilds
➤ Event versioning
➤ Concurrency
➤ Scale
![Page 9: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/9.jpg)
QUICK RECAP
![Page 10: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/10.jpg)
' Event Sourcing ensures that all changes to application state are stored as a sequence of events.
-Martin Fowler
![Page 11: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/11.jpg)
ACTIVE RECORD VS. EVENT SOURCING
Account number
Balance12345678 �€ 50,00
... ...
Money WithdrawnAccount number 12345678
Amount �€ 50,00
Money DepositedAccount number 12345678
Amount �€ 100,00
Account CreatedAccount number 12345678
![Page 12: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/12.jpg)
COMMANDS TO EVENTS
Deposit MoneyAccount number 12345678
Amount �€ 100,00
class DepositMoney { public $accountNumber; public $amount; }
![Page 13: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/13.jpg)
COMMANDS TO EVENTS
Deposit MoneyAccount number 12345678
Amount �€ 100,00
function depositMoney(DepositMoney $command) { $this->apply(new MoneyDeposited( $command->accountNumber, $command->amount, date())); }
command handler
![Page 14: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/14.jpg)
COMMANDS TO EVENTS
Deposit MoneyAccount number 12345678
Amount �€ 100,00
Money DepositedAccount number 12345678
Amount �€ 100,00
class MoneyDeposited { public $accountNumber; public $amount; public $timestamp; }
command handler
![Page 15: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/15.jpg)
AGGREGATES
class BankAccount { public $accountNumber; public $balance; // ... public function applyMoneyDeposited( MoneyDeposited $event) { $this->balance += $event->amount; }
![Page 16: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/16.jpg)
AGGREGATE STATE
Account number
Balance12345678 �€ 0,00
Money WithdrawnAccount number 12345678
Amount �€ 50,00
Money DepositedAccount number 12345678
Amount �€ 100,00
Account CreatedAccount number 12345678
Account number
Balance12345678 �€ 100,00
Account number
Balance12345678 �€ 50,00
![Page 17: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/17.jpg)
CQRS + EVENT SOURCING
Domain
UI
Event Bus
Event Handlers
Command
Repository
Data Layer
Database Database
Event Store
commands
events
events
queries DTOs
Aggregates
![Page 18: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/18.jpg)
PROS AND CONS
➤ Domain fit
➤ Testing
➤ Audit trail
➤ Scalability
➤ Complexity
➤ Library support / maturity
![Page 19: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/19.jpg)
DISCLAIMER
![Page 20: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/20.jpg)
REPLAYS AND REBUILDS
![Page 21: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/21.jpg)
PROJECTIONS AND READ MODELS
User Registered
User Registered
User Unregistered Number of active users?
![Page 22: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/22.jpg)
PROJECTIONS AND READ MODELS
User Registered
User Deactivated
User Reactivated
Number of active users?User Registered
User Unregistered
![Page 23: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/23.jpg)
PROJECTIONS AND READ MODELS
User Registered Event HandlerNumber of
active users +1
User Unregistered Event HandlerNumber of
active users -1
![Page 24: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/24.jpg)
PROJECTIONS AND READ MODELS
Events Event Handler(s) Storage
![Page 25: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/25.jpg)
ELASTICSEARCH
class CompanyRegistered { public $companyId; public $name; public $street; public $city; }
![Page 26: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/26.jpg)
ELASTICSEARCH
function handleCompanyRegistered( CompanyRegistered $event) { $this->elasticsearch->index([ 'index' => 'companies', 'type' => 'company', 'id' => $event->companyId, 'body' => [ 'name' => $event->name, 'address' => [ 'street' => $event->street, 'city' => $event->city ] ] ]); }
![Page 27: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/27.jpg)
READ MODEL UPDATES
➤ New type
➤ New structure
➤ Based on existing events
➤ Generate from scratch?
![Page 28: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/28.jpg)
REBUILDING
Stop application
Remove old read model
Loop over events
Apply to read model
Start application
![Page 29: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/29.jpg)
ZERO DOWNTIME
Loop over existing events
Apply to new read
model
Apply queued events
Start using new read
model
New events Queue
![Page 30: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/30.jpg)
CHALLENGE: LONG RUNNING REBUILDS
➤ Alternatives:
➤ In memory
➤ Distributed
➤ Partial
➤ Background
![Page 31: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/31.jpg)
CHALLENGE: SIDE EFFECTS
User RegisteredUser Id 123abc
Email Address [email protected] Handler
Exclude during replays!
![Page 32: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/32.jpg)
CHALLENGE: TRANSACTIONS
Event Handler
Event Handler
Event
Event Handler
Event Handler ?
![Page 33: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/33.jpg)
CHALLENGE: EVENTUAL CONSISTENCY
➤ Asynchronous event handlers
➤ Reads eventually return the same value
➤ Compare with ACID
➤ UI?
![Page 34: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/34.jpg)
EVENT VERSIONING
![Page 35: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/35.jpg)
DILEMMA
➤ New business requirements
➤ Refactoring
➤ New view on events
![Page 36: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/36.jpg)
NEW EVENTS / VERSIONS
➤ No longer relevant
➤ Renamed
➤ Additional or renamed field(s)
➤ Too coarse, too fine
![Page 37: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/37.jpg)
SUPPORT YOUR LEGACY?
➤ Commands can be renamed
➤ Events are immutable
➤ Correct (incorrect) old events with new events
![Page 38: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/38.jpg)
UPCASTING
Event Store
UserRegistered_V1
Upcaster
UserRegistered_V2
Event Handler
![Page 39: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/39.jpg)
UPCASTING
class UserRegistered_V1 { public $userId; public $name; public $timestamp; }
![Page 40: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/40.jpg)
UPCASTING
class UserRegistered_V2 { public $userId; public $name; public $date; }
![Page 41: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/41.jpg)
UPCASTING
function upcast($event): array { if (!$event instanceof UserRegistered_V1) { return []; } return [ new UserRegistered_V2( $event->userId, $event->name, $event->timestamp->format("Y-m-d")) ]; }
![Page 42: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/42.jpg)
REWRITING HISTORY
Load (subset of) events
Deserialize
Modify
Serialize
Save/replace
![Page 43: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/43.jpg)
THINGS TO BE AWARE OF
Upcasting
➤ Performance
➤ Complexity
➤ Existing projections not automatically updated
Rewriting events
➤ Running code that depends on old structure
➤ Breaking serialization
➤ Changing wrong events
![Page 44: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/44.jpg)
CONCURRENCY
![Page 45: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/45.jpg)
CONCURRENT COMMANDS
Withdraw MoneyAccount number 12345678
Amount �€ 50,00
Deposit MoneyAccount number 12345678
Amount �€ 100,00
?
![Page 46: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/46.jpg)
PESSIMISTIC LOCKING
Withdraw MoneyAccount number 12345678
Amount �€ 50,00
Deposit MoneyAccount number 12345678
Amount �€ 100,00
Account number
Balance12345678 �€ 100,00
Account number
Balance12345678 �€ 50,00
wait for lock
lock
![Page 47: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/47.jpg)
OPTIMISTIC LOCKING
Withdraw MoneyAccount number 12345678
Amount �€ 50,00version 1
Deposit MoneyAccount number 12345678
Amount �€ 100,00version 1
Account number
Balance12345678 �€ 100,00
version 2
ConcurrencyException
![Page 48: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/48.jpg)
SCALE
![Page 49: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/49.jpg)
PERFORMANCE
➤ Server
➤ Database
➤ Framework
➤ Language
➤ Serializer
![Page 50: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/50.jpg)
STORAGE
➤ #events
➤ #aggregates
➤ #events_per_aggregate
➤ Serializer
➤ Event payloads
➤ Costs
![Page 51: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/51.jpg)
SNAPSHOTS
Events1 Account Created2 Money Deposited3 Money Withdrawn4 Money Deposited
SNAPSHOT5 Money Withdrawn
Events1 Account Created2 Money Deposited3 Money Withdrawn4 Money Deposited5 Money Withdrawn
![Page 52: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/52.jpg)
SHARDING
➤ Aggregate Id, Type, Event Timestamp, ...
➤ Rebalancing
➤ Distribution
![Page 53: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/53.jpg)
ARCHIVING EVENTS
➤ Reduce working set
➤ Inactive / deleted aggregates
➤ Historic / irrelevant events
➤ Cheaper storage
![Page 54: CQRS & Event Sourcing in the wild (ScotlandPHP 2016)](https://reader030.vdocuments.mx/reader030/viewer/2022021502/587ac5d21a28abc0478b7d7d/html5/thumbnails/54.jpg)
FRAMEWORK COMPARISON
Framework Upcasting Snapshots Replaying
Broadway (PHP) No (PR) No (PR) Not in core
Prooph (PHP) MessageFactory Yes, triggers on
event countExample code,
off line
Axon (Java/Scala)
Upcaster / UpcasterChain
Yes, triggers on event count
Yes, ReplayingCluster
Akka Persistence (Java/Scala)
Event Adapter Yes, decided by actor Yes