TOTAL RECALL - The application that never forgets

TOTAL RECALL - The application that never forgets

Building an application upon CQRS and Event Sourcing offers three pretty nice benefits.

1. Well structured code and business logic, separated by read and write concerns.
2. The source of truth - A reliable and recoverable view into the past of your application state.
3. The opportunity to answer future questions that you don't know about today.

This talk explains the good fit of two combined application architecture approaches applied to a well known real world example. It shows how to tackle some pitfalls, gives practical advices on infrastructure and exemplifies the aforementioned benefits.

This talk was given at the PHP Central Europe 2018 Conference in Prague, Czech Republic.

8ad631306f5ab343446a967b98e64c0e?s=128

Holger Woltersdorf

October 28, 2018
Tweet

Transcript

  1. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 1 THE APPLICATION THAT NEVER FORGETS
  2. HOLGER WOLTERSDORF CIO • FATHER • PHP DEV WITH ♥

    • MARKDOWN LOVER github.com/hollodotme @hollodotme
  3. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 3 CQRS + EVENT SOURCING
  4. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 4 CQRS (COMMAND QUERY RESPONSIBILITY SEGREGATION)
  5. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF CQS => CQRS 5 ๏ CQRS EVOLVED FROM THE CQS IMPLEMENTATION PATTERN ๏ First stated by Bertrand Meyer in the 1990’s 
 when working on the Eiffel programming language
 ๏ Describes a semantic implementation pattern for object methods ๏ Queries: return results, but do not change the observable state (free of side effects) ๏ Commands: change the state of objects, but do not return values Bertrand Meyer
  6. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF CQS => CQRS 6 class Number { /** @var int */ private $number = 0; public function getIncrementedNumber() : int { $this->number++; return $this->number; } } BAD
  7. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF CQS => CQRS 7 class Number { /** @var int */ private $number = 0; public function increment() : void # Command { $this->number++; } public function getNumber() : int # Query { return $this->number; } } GOOD
  8. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF CQS => CQRS 8 ๏ CQRS IS AN ARCHITECTURE PATTERN ๏ Described by Greg Young’s CQRS documents in 2010 ๏ One part is responsible to change the state of the application ๏ One part is responsible to represent the current state of the application ๏ Command and Query have a broader semantic meaning than in CQS ๏ Key concept is to split your application into: ๏ A write side that is able to process commands (write-only) ๏ A read side that is able to answer queries (read-only) ๏ Give each side an appropriate storage GREG YOUNG
  9. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 9 APPLICATION?
  10. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 10 WEB APPLICATION LAYERS REQUEST HANDLING LAYER BUSINESS LOGIC LAYER PERSISTENCE LAYER
  11. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 11 WEB APPLICATION LAYERS REQUEST HANDLING LAYER BUSINESS LOGIC LAYER PERSISTENCE LAYER KNOWS HTTP
  12. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 12 WEB APPLICATION LAYERS REQUEST HANDLING LAYER BUSINESS LOGIC LAYER PERSISTENCE LAYER KNOWS HTTP KNOWS BUSINESS RULES
  13. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 13 WEB APPLICATION LAYERS REQUEST HANDLING LAYER BUSINESS LOGIC LAYER PERSISTENCE LAYER KNOWS HTTP KNOWS BUSINESS RULES KNOWS DATABASE
  14. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 14 WEB APPLICATION LAYERS REQUEST HANDLING LAYER BUSINESS LOGIC LAYER PERSISTENCE LAYER CQRS CAN BE APPLIED TO ALL OF THEM KNOWS HTTP KNOWS BUSINESS RULES KNOWS DATABASE
  15. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 15 WEB APPLICATION LAYERS READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE AFTER CQRS WAS APPLIED VIEW LOGIC
  16. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 16 WEB APPLICATION LAYERS READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE HOW DO WE GET FROM HERE… … TO THERE? VIEW LOGIC
  17. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 17 WEB APPLICATION LAYERS READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE PUBLISH SUBSCRIBE REDIRECT (+ SESSION) VIEW LOGIC
  18. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF REPRESENTATION 18 WEB APPLICATION LAYERS READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE VIEW LOGIC PUBLISH SUBSCRIBE REDIRECT (+ SESSION) REPRESENTATION MESSAGE BROKER ASYNC EVENTUAL CONSISTENCY !
  19. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF REPRESENTATION 19 WEB APPLICATION LAYERS READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE VIEW LOGIC PUBLISH SUBSCRIBE REDIRECT (+ SESSION) REPRESENTATION PHP FASTCGI CLIENT ASYNC EVENTUAL CONSISTENCY !
  20. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF REPRESENTATION 20 WEB APPLICATION LAYERS READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE VIEW LOGIC PUBLISH SUBSCRIBE REPRESENTATION MESSAGE BROKER
  21. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF REPRESENTATION 21 WEB APPLICATION LAYERS READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE VIEW LOGIC PUBLISH SUBSCRIBE REPRESENTATION MESSAGE BROKER
  22. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF REPRESENTATION 22 WEB APPLICATION LAYERS READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE VIEW LOGIC PUBLISH SUBSCRIBE REPRESENTATION MESSAGE BROKER
  23. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 23 CQRS (REQUEST HANDLING LAYER) ๏ GET-REQUEST ๏ NO VIEW LOGIC ๏ COULD BE A STATIC
 HTML PAGE ๏ SUBMIT TRIGGERS
 POST REQUEST
  24. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 24 WEB APPLICATION LAYERS READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE REDIRECT (+ SESSION) VIEW LOGIC POST GET x » ERRORS »
  25. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 25 CQRS (REQUEST HANDLING LAYER) ๏ GET-REQUEST 
 AFTER REDIRECT ๏ CHECK FOR ERRORS 
 IN SESSION ๏ ERRORS RENDERED INTO VIEW ๏ ONLY SUBMIT 
 TRIGGERS POST REQUEST ๏ RELOAD DOESN’T
  26. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 26 WEB APPLICATION LAYERS READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE VIEW LOGIC GET
  27. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 27 WEB APPLICATION LAYERS READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE VIEW LOGIC GET GET GET GET
  28. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF READ-/WRITE-REQUESTS 28 5 % 95 % Reads Writes FOR A FASHION ONLINE SHOP
  29. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF CQRS (REQUEST HANDLING LAYER) 29 final class NewsletterSubscriptionValidator implements MiddlewareInterface { /** @var ValidatorInterface !*/ private $validator; /** @var SessionInterface !*/ private $session; public function !__construct( ValidatorInterface $validator, SessionInterface $session ) { $this!->validator = $validator; $this!->session = $session; } public function process( ServerRequestInterface $request, RequestHandlerInterface $handler ) : ResponseInterface { $this!->validator!->checkUserInput( $request ); if ( $this!->validator!->failed() ) { $this!->session!->addErrors( 'initSubscription', $this!->validator!->getErrors() ); return Redirect!::to( '/subscription-form' ); } return $handler!->handle( $request ); } }
  30. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF CQRS (REQUEST HANDLING LAYER) 30 final class NewsletterSubscriptionForm implements MiddlewareInterface { /** @var SessionInterface !*/ private $session; public function !__construct( SessionInterface $session ) { $this!->session = $session; } public function process( ServerRequestInterface $request, RequestHandlerInterface $handler ) : ResponseInterface { return Page!::fromTemplate( 'subscription-form' ) !->withErrors( $this!->session!->getErrors( 'initSubscription' ) ); } }
  31. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 31 CQRS (REQUEST HANDLING LAYER) ๏ GET-REQUEST ๏ NO VIEW LOGIC ๏ COULD BE A STATIC
 HTML PAGE ๏ CAN BE DELETED 
 AFTER SUBSCRIPTION
 IS FINISHED
  32. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF CQRS (REQUEST HANDLING LAYER) 32 final class SubscriptionInitializer implements MiddlewareInterface { /** @var InitSubscriptionCommandHandler !*/ private $commandHandler; /** @var MessageBrokerInterface !*/ private $messageBroker; public function !__construct( InitSubscriptionCommandHandler $commandHandler, MessageBrokerInterface $messageBroker ) { $this!->commandHandler = $commandHandler; $this!->messageBroker = $messageBroker; } public function process( ServerRequestInterface $request, RequestHandlerInterface $handler ) : ResponseInterface { $command = InitSubscriptionCommand!::withFullNameAndEmail( Fullname!::fromString( $request!->getParsedBody()['fullName'] ), Email!::fromString( $request!->getParsedBody()['email'] ) ); $result = $this!->commandHandler!->handle( $command ); $message = SubscriptionInitialized!::withSubscription( $result!->getSubscription() ); $this!->messageBroker!->publish( $message ); return Redirect!::to( '/thanks-' . $result!->getSubscription()!->getId() . '.html' ); } }
  33. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF CQRS (REQUEST HANDLING LAYER) 33 final class SubscriptionInitializer implements MiddlewareInterface { /** @var InitSubscriptionCommandHandler !*/ private $commandHandler; /** @var MessageBrokerInterface !*/ private $messageBroker; public function !__construct( InitSubscriptionCommandHandler $commandHandler, MessageBrokerInterface $messageBroker ) { $this!->commandHandler = $commandHandler; $this!->messageBroker = $messageBroker; } public function process( ServerRequestInterface $request, RequestHandlerInterface $handler ) : ResponseInterface { $command = InitSubscriptionCommand!::withFullNameAndEmail( Fullname!::fromString( $request!->getParsedBody()['fullName'] ), Email!::fromString( $request!->getParsedBody()['email'] ) ); $result = $this!->commandHandler!->handle( $command ); $message = SubscriptionInitialized!::withSubscription( $result!->getSubscription() ); $this!->messageBroker!->publish( $message ); return Redirect!::to( '/thanks-' . $result!->getSubscription()!->getId() . '.html' ); } } ENTERING BUSINESS LOGIC LAYER & CHANGING STATE
  34. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF CQRS (REQUEST HANDLING LAYER) 34 final class SubscriptionInitializer implements MiddlewareInterface { /** @var InitSubscriptionCommandHandler !*/ private $commandHandler; /** @var MessageBrokerInterface !*/ private $messageBroker; public function !__construct( InitSubscriptionCommandHandler $commandHandler, MessageBrokerInterface $messageBroker ) { $this!->commandHandler = $commandHandler; $this!->messageBroker = $messageBroker; } public function process( ServerRequestInterface $request, RequestHandlerInterface $handler ) : ResponseInterface { $command = InitSubscriptionCommand!::withFullNameAndEmail( Fullname!::fromString( $request!->getParsedBody()['fullName'] ), Email!::fromString( $request!->getParsedBody()['email'] ) ); $result = $this!->commandHandler!->handle( $command ); $message = SubscriptionInitialized!::withSubscription( $result!->getSubscription() ); $this!->messageBroker!->publish( $message ); return Redirect!::to( '/thanks-' . $result!->getSubscription()!->getId() . '.html' ); } } TRIGGER NEW READ REPRESENTATION
  35. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF REPRESENTATION 35 WEB APPLICATION LAYERS READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE VIEW LOGIC PUBLISH SUBSCRIBE REPRESENTATION MESSAGE BROKER
  36. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF CQRS (BUSINESS LOGIC) 36 final class InitSubscriptionCommand { /** @var FullName !*/ private $fullName; /** @var Email !*/ private $email; private function !__construct( FullName $fullName, Email $email ) { $this!->fullName = $fullName; $this!->email = $email; } public static function withFullNameAndEmail( FullName $fullName, Email $email ) : self { return new self( $fullName, $email ); } public function getFullName() : FullName { return $this!->fullName; } public function getEmail() : Email { return $this!->email; } }
  37. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF CQRS (BUSINESS LOGIC) 37 final class InitSubscriptionCommandHandler { /** @var SubscriptionRepository !*/ private $repository; public function !__construct( SubscriptionRepository $repository ) { $this!->repository = $repository; } public function handle( InitSubscriptionCommand $command ) : InitSubscriptionResult { try { $subscription = Subscription!::fromIdFullNameAndEmail( SubscriptionId!::generate(), $command!->getFullName(), $command!->getEmail() ); $this!->repository!->add( $subscription ); return InitSubscriptionResult!::withSubscription( $subscription ); } catch ( Throwable $e ) { return InitSubscriptionResult!::withErrorMessage( $e!->getMessage() ); } } }
  38. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF CQRS (BUSINESS LOGIC) 38 final class InitSubscriptionResult { /** @var bool !*/ private $succeeded = false; /** @var string !*/ private $errorMessage = ''; /** @var Subscription !*/ private $subscription; public static function withSubscription( Subscription $subscription ) : self { $result = new self(); $result!->succeeded = true; $result!->subscription = $subscription; return $result; } public static function withErrorMessage( string $errorMessage ) : self { $result = new self(); $result!->errorMessage = $errorMessage; return $result; } }
  39. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF CQRS (PRESENTATION) 39 final class SubscriptionInitializedSubscriber { public function notify( SubscriptionInitialized $message ) : void { $subscription = $message!->getSubscription(); Page!::fromTemplate( 'thanks' ) !->withData( ['subscription' !=> $subscription] ) !->saveAsHtml( '/thanks-' . $subscription!->getId() . '.html' ); } } final class SendConfirmationMailSubscriber { public function notify( SubscriptionInitialized $message ) : void { $subscription = $message!->getSubscription(); Email!::fromTemplate( 'confirm-subscription' ) !->withData( ['subscription' !=> $subscription] ) !->to( $subscription!->getEmail() ) !->send(); } }
  40. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 40 CQRS (REQUEST HANDLING LAYER) ๏ E-MAIL ๏ CALL TO ACTION
 FOR A STATE CHANGE ๏ POST REQUESTS
 NOT AVAILABLE 
 FROM E-MAILS!
  41. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 41 RESPECT HTTP VERBS! CQRS (REQUEST HANDLING LAYER)
  42. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 42 […] the convention has been established that the GET and HEAD methods SHOULD NOT have the significance of taking an action other than retrieval. These methods ought to be considered "safe". https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html CQRS (REQUEST HANDLING LAYER)
  43. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 43 CQRS (REQUEST HANDLING LAYER) ๏ DON’T CHANGE STATE 
 BY READ REQUESTS! ๏ GET-REQUEST ๏ NO VIEW LOGIC ๏ COULD BE A STATIC
 HTML PAGE, PREPARED BASED ON SUCCESSFUL 
 INIT RESULT ๏ SUBMIT TRIGGERS 
 POST REQUEST ๏ CAN BE DELETED 
 AFTER SUBSCRIPTION
 IS FINISHED
  44. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 44 CQRS (REQUEST HANDLING LAYER) ๏ GET-REQUEST
 AFTER REDIRECT ๏ NO VIEW LOGIC ๏ COULD BE A STATIC
 HTML PAGE, PREPARED BASED ON SUCCESSFUL 
 FINISH RESULT
  45. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 45 PERSISTENCE LAYER & STATE SubscriberID Fullname Email Interval Subscribed Confirmed Unsubscribed ‹UUID-HW› Holger Woltersdorf hollodotme @PHPkonf.org ONLY WHEN PHPCE happens 2018-05-19 2018-05-20 2018-05-21 ‹UUID-JD› Jane Doe jane@doe.com Monthly 2018-03-01 NULL NULL SubscriberID Topic ‹UUID-HW› HIPSTER JEANS ‹UUID-HW› NERD CAPS ‹UUID-HW› MEME SHIRTS
  46. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 46 PERSISTENCE LAYER & STATE WHAT HAPPENS, IF… I subscribe again for MEME SHIRTS only with WEEKLY interval
  47. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 47 PERSISTENCE LAYER & STATE SubscriberID Fullname Email Interval Subscribed Confirmed Unsubscribed ‹UUID-HW› Holger Woltersdorf hollodotme @PHPkonf.org Weekly 2018-05-22 2018-05-23 NULL ‹UUID-JD› Jane Doe jane@doe.com Monthly 2018-03-01 NULL NULL SubscriberID Topic ‹UUID-HW› MEME SHIRTS
  48. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 48 PERSISTENCE LAYER & STATE WORKS, RIGHT?
  49. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 49 PERSISTENCE LAYER & STATE … SINCERLY, YOUR BUSINESS DATA ANALYST
  50. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF CQRS (PERSISTENCE LAYER - STATE) 50 What have we lost? ACTUALLY ALL INFORMATION ABOUT THE FIRST SUBSCRIPTION except e-mail & full name
  51. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF CQRS (PERSISTENCE LAYER - STATE) 51 What else have we lost? ANALYSABLE DATA ABOUT THE USER’S DECISION TO REACTIVATE AND CHANGE SUBSCRIPTION
  52. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 52 CQRS (PERSISTENCE LAYER - STATE) The application state is observable, BUT ONLY AT THE EXACT POINT OF TIME we look at it.
  53. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 53 + EVENT SOURCING
  54. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF EVENT SOURCING 54 ๏ EVENT SOURCING IS AN ANALYSIS PATTERN ๏ Article by Martin Fowler in 2005 ๏ CQRS + Event Sourcing also part of Greg Young’s CQRS documents ๏ Pattern was already used in accounting software way earlier ๏ Key concept: 
 
 "Record all changes to application state as a sequence of events."
 — Martin Fowler GREG YOUNG MARTIN FOWLER
  55. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 55 INSTALLING EVENT SOURCING READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE PUBLISH SUBSCRIBE REDIRECT (+ SESSION) VIEW LOGIC
  56. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 56 INSTALLING EVENT SOURCING READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE PUBLISH SUBSCRIBE REDIRECT (+ SESSION) VIEW LOGIC
  57. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 57 INSTALLING EVENT SOURCING READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS EVENT STORE PUBLISH SUBSCRIBE REDIRECT (+ SESSION) VIEW LOGIC READ HISTORY OF CHANGES RECORD NEW CHANGES
  58. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF EVENT SOURCING 58 BUSINESS ENTITY EVENTSTORE COMMAND TRIGGER CHANGES EMITS EVENTS (ALL NEW CHANGES) BUILT FROM EVENT STREAM (HISTORY OF CHANGES) PUBLISH NEWLY RECORDED EVENTS
  59. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 59 EVENT STORE Seq StreamType StreamID StreamSeq EventID Payload Occurred MetaX Meta… 10 Newsletter
 Subscription ‹UUID-HW› 1 Subscription was initiated {"json"} 2017-05-21 T12:13:14 SHOP TY … 11 Newsletter
 Subscription ‹UUID-HW› 2 Interval was changed {"json"} 2017-05-22 T10:09:08 SHOP TY … 12 Newsletter
 Subscription ‹UUID-HW› 3 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 13 Newsletter Subscription ‹UUID-HW› 4 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 14 Newsletter
 Subscription ‹UUID-HW› 5 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 15 Newsletter
 Subscription ‹UUID-HW› 6 Subscription was confirmed {"json"} 2017-05-22 T10:09:08 SHOP TY … 33 Newsletter
 Subscription ‹UUID-HW› 7 Subscription was canceled {"json"} 2017-05-25 T07:09:09 SHOP DE … 50 Newsletter
 Subscription ‹UUID-HW› 8 Subscription was reactivated {"json"} 2017-06-01 T14:15:16 SHOP DE … 51 Newsletter
 Subscription ‹UUID-HW› 9 Topic was removed {"json"} 2017-06-01 T14:15:16 SHOP DE … 52 Newsletter Subscription ‹UUID-HW› 10 Topic was removed {"json"} 2017-06-01 T14:15:16 SHOP DE … 53 Newsletter
 Subscription ‹UUID-HW› 11 Interval was changed {"json"} 2017-06-01 T14:15:16 SHOP DE … 54 Newsletter
 Subscription ‹UUID-HW› 12 Subscription was confirmed {"json"} 2017-06-02 T16:17:18 SHOP DE …
  60. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF Seq StreamType StreamID StreamSeq EventID Payload Occurred MetaX Meta… 10 Newsletter
 Subscription ‹UUID-HW› 1 Subscription was initiated {"json"} 2017-05-21 T12:13:14 SHOP TY … 11 Newsletter
 Subscription ‹UUID-HW› 2 Interval was changed {"json"} 2017-05-22 T10:09:08 SHOP TY … 12 Newsletter
 Subscription ‹UUID-HW› 3 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 13 Newsletter Subscription ‹UUID-HW› 4 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 14 Newsletter
 Subscription ‹UUID-HW› 5 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 15 Newsletter
 Subscription ‹UUID-HW› 6 Subscription was confirmed {"json"} 2017-05-22 T10:09:08 SHOP TY … 33 Newsletter
 Subscription ‹UUID-HW› 7 Subscription was canceled {"json"} 2017-05-25 T07:09:09 SHOP DE … 50 Newsletter
 Subscription ‹UUID-HW› 8 Subscription was reactivated {"json"} 2017-06-01 T14:15:16 SHOP DE … 51 Newsletter
 Subscription ‹UUID-HW› 9 Topic was removed {"json"} 2017-06-01 T14:15:16 SHOP DE … 52 Newsletter Subscription ‹UUID-HW› 10 Topic was removed {"json"} 2017-06-01 T14:15:16 SHOP DE … 53 Newsletter
 Subscription ‹UUID-HW› 11 Interval was changed {"json"} 2017-06-01 T14:15:16 SHOP DE … 54 Newsletter
 Subscription ‹UUID-HW› 12 Subscription was confirmed {"json"} 2017-06-02 T16:17:18 SHOP DE … 60 EVENT STORE SEQUENCE SORTING ALL EVENTS IN THE EVENT STORE (AUTO-INCREMENT)
  61. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF Seq StreamType StreamID StreamSeq EventID Payload Occurred MetaX Meta… 10 Newsletter
 Subscription ‹UUID-HW› 1 Subscription was initiated {"json"} 2017-05-21 T12:13:14 SHOP TY … 11 Newsletter
 Subscription ‹UUID-HW› 2 Interval was changed {"json"} 2017-05-22 T10:09:08 SHOP TY … 12 Newsletter
 Subscription ‹UUID-HW› 3 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 13 Newsletter Subscription ‹UUID-HW› 4 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 14 Newsletter
 Subscription ‹UUID-HW› 5 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 15 Newsletter
 Subscription ‹UUID-HW› 6 Subscription was confirmed {"json"} 2017-05-22 T10:09:08 SHOP TY … 33 Newsletter
 Subscription ‹UUID-HW› 7 Subscription was canceled {"json"} 2017-05-25 T07:09:09 SHOP DE … 50 Newsletter
 Subscription ‹UUID-HW› 8 Subscription was reactivated {"json"} 2017-06-01 T14:15:16 SHOP DE … 51 Newsletter
 Subscription ‹UUID-HW› 9 Topic was removed {"json"} 2017-06-01 T14:15:16 SHOP DE … 52 Newsletter Subscription ‹UUID-HW› 10 Topic was removed {"json"} 2017-06-01 T14:15:16 SHOP DE … 53 Newsletter
 Subscription ‹UUID-HW› 11 Interval was changed {"json"} 2017-06-01 T14:15:16 SHOP DE … 54 Newsletter
 Subscription ‹UUID-HW› 12 Subscription was confirmed {"json"} 2017-06-02 T16:17:18 SHOP DE … 61 EVENT STORE StreamType NAME OF THE BUSINESS ENTITY StreamID UNIQUE ID OF SPECIFIC BUSINESS ENTITY StreamSeq VERSION OF THE SPECIFIC BUSINESS ENTITY INSTANCE (USED TO SORT THE ENTITY’S EVENT STREAM)
  62. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF Seq StreamType StreamID StreamSeq EventID Payload Occurred MetaX Meta… 10 Newsletter
 Subscription ‹UUID-HW› 1 Subscription was initiated {"json"} 2017-05-21 T12:13:14 SHOP TY … 11 Newsletter
 Subscription ‹UUID-HW› 2 Interval was changed {"json"} 2017-05-22 T10:09:08 SHOP TY … 12 Newsletter
 Subscription ‹UUID-HW› 3 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 13 Newsletter Subscription ‹UUID-HW› 4 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 14 Newsletter
 Subscription ‹UUID-HW› 5 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 15 Newsletter
 Subscription ‹UUID-HW› 6 Subscription was confirmed {"json"} 2017-05-22 T10:09:08 SHOP TY … 33 Newsletter
 Subscription ‹UUID-HW› 7 Subscription was canceled {"json"} 2017-05-25 T07:09:09 SHOP DE … 50 Newsletter
 Subscription ‹UUID-HW› 8 Subscription was reactivated {"json"} 2017-06-01 T14:15:16 SHOP DE … 51 Newsletter
 Subscription ‹UUID-HW› 9 Topic was removed {"json"} 2017-06-01 T14:15:16 SHOP DE … 52 Newsletter Subscription ‹UUID-HW› 10 Topic was removed {"json"} 2017-06-01 T14:15:16 SHOP DE … 53 Newsletter
 Subscription ‹UUID-HW› 11 Interval was changed {"json"} 2017-06-01 T14:15:16 SHOP DE … 54 Newsletter
 Subscription ‹UUID-HW› 12 Subscription was confirmed {"json"} 2017-06-02 T16:17:18 SHOP DE … 62 EVENT STORE EventID NAME OF THE CHANGE (USED TO IDENTIFY EVENT CLASS) Payload ALL RELEVANT DATA THAT IS NEEDED TO REPRODUCE THE CHANGE USE ONE TECHNOLOGY INDEPENDENT DATA FORMAT! Occurred TIMESTAMP WHEN THE CHANGE WAS RECORDED DO NOT USE THIS FOR SORTING EVENT STREAMS DO NOT RELY ON TIMESTAMPS! IT’S JUST META DATA
  63. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF Seq StreamType StreamID StreamSeq EventID Payload Occurred MetaX Meta… 10 Newsletter
 Subscription ‹UUID-HW› 1 Subscription was initiated {"json"} 2017-05-21 T12:13:14 SHOP TY … 11 Newsletter
 Subscription ‹UUID-HW› 2 Interval was changed {"json"} 2017-05-22 T10:09:08 SHOP TY … 12 Newsletter
 Subscription ‹UUID-HW› 3 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 13 Newsletter Subscription ‹UUID-HW› 4 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 14 Newsletter
 Subscription ‹UUID-HW› 5 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 15 Newsletter
 Subscription ‹UUID-HW› 6 Subscription was confirmed {"json"} 2017-05-22 T10:09:08 SHOP TY … 33 Newsletter
 Subscription ‹UUID-HW› 7 Subscription was canceled {"json"} 2017-05-25 T07:09:09 SHOP DE … 50 Newsletter
 Subscription ‹UUID-HW› 8 Subscription was reactivated {"json"} 2017-06-01 T14:15:16 SHOP DE … 51 Newsletter
 Subscription ‹UUID-HW› 9 Topic was removed {"json"} 2017-06-01 T14:15:16 SHOP DE … 52 Newsletter Subscription ‹UUID-HW› 10 Topic was removed {"json"} 2017-06-01 T14:15:16 SHOP DE … 53 Newsletter
 Subscription ‹UUID-HW› 11 Interval was changed {"json"} 2017-06-01 T14:15:16 SHOP DE … 54 Newsletter
 Subscription ‹UUID-HW› 12 Subscription was confirmed {"json"} 2017-06-02 T16:17:18 SHOP DE … 63 EVENT STORE ADDITIONAL META DATA BUSINESS IRRELEVANT DEPENDS ON YOUR DOMAIN AND INFRASTRUCTURE FOR EXAMPLE SHOP INSTANCE SERVER INSTANCE AUTH-USER (IF NOT BUSINESS RELEVANT) APPLICATION VERSION ETC. ADD WHATEVER YOU NEED
  64. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF Seq StreamType StreamID StreamSeq EventID Payload Occurred MetaX Meta… 10 Newsletter
 Subscription ‹UUID-HW› 1 Subscription was initiated {"json"} 2017-05-21 T12:13:14 SHOP TY … 11 Newsletter
 Subscription ‹UUID-HW› 2 Interval was changed {"json"} 2017-05-22 T10:09:08 SHOP TY … 12 Newsletter
 Subscription ‹UUID-HW› 3 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 13 Newsletter Subscription ‹UUID-HW› 4 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 14 Newsletter
 Subscription ‹UUID-HW› 5 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 15 Newsletter
 Subscription ‹UUID-HW› 6 Subscription was confirmed {"json"} 2017-05-22 T10:09:08 SHOP TY … 33 Newsletter
 Subscription ‹UUID-HW› 7 Subscription was canceled {"json"} 2017-05-25 T07:09:09 SHOP DE … 50 Newsletter
 Subscription ‹UUID-HW› 8 Subscription was reactivated {"json"} 2017-06-01 T14:15:16 SHOP DE … 51 Newsletter
 Subscription ‹UUID-HW› 9 Topic was removed {"json"} 2017-06-01 T14:15:16 SHOP DE … 52 Newsletter Subscription ‹UUID-HW› 10 Topic was removed {"json"} 2017-06-01 T14:15:16 SHOP DE … 53 Newsletter
 Subscription ‹UUID-HW› 11 Interval was changed {"json"} 2017-06-01 T14:15:16 SHOP DE … 54 Newsletter
 Subscription ‹UUID-HW› 12 Subscription was confirmed {"json"} 2017-06-02 T16:17:18 SHOP DE … 64 EVENT STORE FIRST SUBSCRIPTION PLEASE NOTE: GIVING BIRTH TO A NEW BUSINESS ENTITY IS A CHANGE TO APPLICATION STATE
  65. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF Seq StreamType StreamID StreamSeq EventID Payload Occurred MetaX Meta… 10 Newsletter
 Subscription ‹UUID-HW› 1 Subscription was initiated {"json"} 2017-05-21 T12:13:14 SHOP TY … 11 Newsletter
 Subscription ‹UUID-HW› 2 Interval was changed {"json"} 2017-05-22 T10:09:08 SHOP TY … 12 Newsletter
 Subscription ‹UUID-HW› 3 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 13 Newsletter Subscription ‹UUID-HW› 4 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 14 Newsletter
 Subscription ‹UUID-HW› 5 Topic was assigned {"json"} 2017-05-22 T10:09:08 SHOP TY … 15 Newsletter
 Subscription ‹UUID-HW› 6 Subscription was confirmed {"json"} 2017-05-22 T10:09:08 SHOP TY … 33 Newsletter
 Subscription ‹UUID-HW› 7 Subscription was canceled {"json"} 2017-05-25 T07:09:09 SHOP DE … 50 Newsletter
 Subscription ‹UUID-HW› 8 Subscription was reactivated {"json"} 2017-06-01 T14:15:16 SHOP DE … 51 Newsletter
 Subscription ‹UUID-HW› 9 Topic was removed {"json"} 2017-06-01 T14:15:16 SHOP DE … 52 Newsletter Subscription ‹UUID-HW› 10 Topic was removed {"json"} 2017-06-01 T14:15:16 SHOP DE … 53 Newsletter
 Subscription ‹UUID-HW› 11 Interval was changed {"json"} 2017-06-01 T14:15:16 SHOP DE … 54 Newsletter
 Subscription ‹UUID-HW› 12 Subscription was confirmed {"json"} 2017-06-02 T16:17:18 SHOP DE … 65 EVENT STORE SECOND SUBSCRIPTION PLEASE NOTE: EXPRESS EXACTLY WHAT YOU ARE DOING INITIATED !== REACTIVATED AVOID AGGREGATION OF CHANGES YOU’LL LOSE VALUABLE INFORMATION Topics were changed !== Topic was added + Topic was removed
  66. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF EVENT STORE PAYLOAD 66 { "SubscriptionID": "‹UUID-HW›", "Fullname": "Holger Woltersdorf", "Email": "hollodotme@PHPkonf.org" } "Subscription was initialised" { "SubscriptionID": "‹UUID-HW›" } vs. "Subscription was reactivated"
  67. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF EVENT STORE PAYLOAD 67 DO NOT ADD PREVIOUS VALUES TO PAYLOAD IT SHOULD HAVE BEEN RECORDED BEFORE STOP THINKING RELATIONAL START THINKING TEMPORAL
  68. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF EVENT SOURCING 68 interface EventInterface { public function getStreamID() : StreamIDInterface public function getEventID() : EventID public function getPayload() : Payload public static function fromPayload( Payload $payload ) : EventInterface } interface EventEnvelopeInterface { public function getHeader() : EventHeaderInterface public function getEvent() : EventInterface }
  69. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF EVENT SOURCING 69 interface EventHeaderInterface { public function getStreamType() : StreamType public function getStreamID() : StreamIDInterface public function getStreamSeq() : StreamSequence public function getOccurred() : Timestamp public function getMetaX() : ShopName public function getMeta…() : … }
  70. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF EVENT SOURCING 70 ๏ EVENT STORE IS IMMUTABLE AND APPEND-ONLY ๏ EVENT STORE IS THE ONLY SOURCE OF TRUTH ๏ USE TECHNOLOGY INDEPENDENT PAYLOAD FORMAT (XML/JSON/ETC.)
 DO NOT USE OBJECT SERIALISATION! ๏ EVENTS ARE IMMUTABLE ๏ SEPARATE PAYLOAD FROM META DATA (EVENT ENVELOPE) ๏ VALUE OBJECTS ARE IMMUTABLE ๏ EVENT STREAM IS A SORTED LIST OF EVENTS ๏ BUSINESS ENTITIES CAN NOT BE INSTANTIATED WITHOUT EVENT STREAM
  71. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF EVENT SOURCING (BUSINESS ENTITY) 71 class NewsletterSubscription { private $streamType; private $streamSeq; private $changes; # ... private function __construct() { $this->streamType = new StreamType( 'Newsletter Subscription' ); $this->streamSeq = new StreamSeq( 0 ); $this->changes = new EventStream(); } # ... }
  72. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF EVENT SOURCING (BUSINESS ENTITY) 72 class NewsletterSubscription { public static function initiate( Email $email, Fullname $fullname ) : NewsletterSubscription { $subscription = new self(); $subscription->recordThat( new SubscriptionWasInitiated( SubscriptionID::generate(), $email, $fullname ) ); return $subscription; } # ... } ๏ DO NOT CREATE ENTITIES WITHOUT RECORDING AN EVENT
  73. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF EVENT SOURCING (BUSINESS ENTITY) 73 class NewsletterSubscription { #... private function recordThat( EventInterface $event ) : void { $envelope = EnvelopeBuilder::build( $this->getStreamType(), $this->streamSeq->increment(), $event ); $this->changes->append( $envelope ); $this->apply( EventEnvelopeInterface $envelope ); } #... }
  74. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF EVENT SOURCING (BUSINESS ENTITY) 74 class NewsletterSubscription { #... private function apply( EventEnvelopeInterface $envelope ) : void { $header = $envelope->getHeader(); $event = $envelope->getEvent(); $this->streamSeq = $header->getStreamSeq(); $methodName = 'when' . $event->getEventID()->toUpperCamelCase(); $this->$methodName( $event ); # $this->whenSubscriptionWasInitiated( $event ); } #... }
  75. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF EVENT SOURCING (BUSINESS ENTITY) 75 class NewsletterSubscription { #... protected function whenSubscriptionWasInitiated( SubscriptionWasInitiated $event ) : void { $this->email = $event->getEmail(); $this->fullname = $event->getFullname(); # This is your "real" constructor # Init properties here } #... }
  76. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF EVENT SOURCING (BUSINESS ENTITY) 76 class NewsletterSubscription { #... public static function reconstitute( EventStream $eventStream ) : self { $subscription = new self(); foreach ( $eventStream as $envelope ) { $subscription->apply( $envelope ); } return $subscription; } #... }
  77. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF EVENT SOURCING (BUSINESS ENTITY) 77 abstract class AbstractBusinessEntity { final protected function __construct() final protected function recordThat( EventInterface $event ): void private function apply( EventEnvelopeInterface $envelope ) : void final public static function reconstitute( EventStream $eventStream ) }
  78. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF EVENT SOURCING (BUSINESS ENTITY) 78 class NewsletterSubscription extends AbstractBusinessEntity { private $topics; public function assignTopic( Topic $topic ) : void protected function whenTopicWasAssigned( TopicWasAssigned $event ) : void } class NewsletterSubscription extends AbstractBusinessEntity { use NewsletterSubscriptionChanging; public function assignTopic( Topic $topic ) : void } USE A TRAIT TO EXPOSE PUBLIC API ONLY trait NewsletterSubscriptionChanging { private $topics; protected function whenTopicWasAssigned( TopicWasAssigned $event ) : void }
  79. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF EVENT SOURCING 79 ๏ QUESTIONS WE CAN ASK NOW: ๏ WHAT WAS THE (UN-)SUBSCRIBE RATIO IN 2016? ๏ HOW MANY USERS REACTIVATED THEIR SUBSCRIPTION IN 2017? ๏ HOW MANY USERS REACTIVATED MORE THAN ONCE? ๏ WHICH TOPIC WAS REMOVED THE MOST? ๏ HOW MANY USERS CHANGED INTERVAL BEFORE SUBSCRIPTION WAS CANCELED?
  80. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 80 EVENT SOURCING READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS EVENT STORE PUBLISH SUBSCRIBE REDIRECT (+ SESSION) VIEW LOGIC READ HISTORY OF CHANGES RECORD NEW CHANGES
  81. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF RECORD NEW CHANGES READ HISTORY OF CHANGES 81 EVENT SOURCING READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS EVENT STORE PUBLISH SUBSCRIBE REDIRECT (+ SESSION) VIEW LOGIC
  82. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF RECORD NEW CHANGES READ HISTORY OF CHANGES 82 EVENT SOURCING (PROJECTION) READ REQUESTS BUSINESS LOGIC PROJECTION WRITE REQUESTS EVENT STORE PUBLISH SUBSCRIBE REDIRECT (+ SESSION) VIEW LOGIC
  83. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF RECORD NEW CHANGES READ HISTORY OF CHANGES 83 EVENT SOURCING (PROJECTION) READ REQUESTS BUSINESS LOGIC WRITE REQUESTS REDIRECT (+ SESSION) VIEW LOGIC REPRESENTATION REPRESENTATION EVENT STORE PUBLISH SUBSCRIBE PROJECTION MESSAGE BROKER ASYNC EVENTUAL CONSISTENCY !
  84. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF EVENT SOURCING (PROJECTING) 84 ๏ PUSH VIEWS ๏ Gets notified about a new change (event) ๏ Can directly project this information ๏ PULL VIEWS ๏ Gets a query and pulls up appropriate event streams to gather information ๏ Can respond or project the result ๏ MIXED VIEWS ๏ Gets notified and gathers missing information from event store ๏ Projects this information
  85. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF EVENT SOURCING (PROJECTING) 85 PLEASE NOTE: PROJECTIONS SHOULD CONVERT STATE INFORMATION TO THE BEST DATA FORMAT FOR YOUR READ-USE-CASE THIS COULD ALSO BE ANOTHER EVENT STORE WITH A SUBSET OF STREAMS / EVENTS
  86. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF CQRS ADVANTAGES 86 ๏ WHAT CAN WE GAIN FROM APPLYING CQRS? ๏ Separate input / output handling ๏ Opportunity to optimise each side for its operational purpose ๏ Depends on your application domain ๏ e.g. eCommerce Shops usually have a read / write ratio of 95% / 5%
 so optimisation for reading is a good idea ๏ Clean code, reusable queries and commands, decoupling, simpler tests ๏ No unintended side effects ๏ Technology independence for each side ๏ Good prerequisite for individual horizontal scalability
  87. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF EVENT SOURCING ADVANTAGES 87 ๏ WHAT CAN WE GAIN FROM USING EVENT SOURCING? ๏ High scalability ๏ Simplification of storing application state ("one size fits all") ๏ Traceability of all changes ever made to application state ๏ Retroactive data analysis at any time ๏ Adapting new business requirements with ease ๏ Restoring state representation at any given point in time ๏ Replay after bug fixes to re-establish view consistency ๏ Easy backup & restore
  88. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF 88 QUESTIONS?
  89. THANK YOU! github.com/hollodotme @hollodotme / @F9T3ch fortuneglobe.com phpug-dresden.org @phpugdd HOLGER

    WOLTERSDORF speakerdeck.com/hollodotme Slides available at:
  90. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • OCT

    28, 2018 • PHPCE. • PRAGUE HOLGER WOLTERSDORF LINKS / REFERENCES 90 ๏ CQS: ๏ https://martinfowler.com/bliki/CommandQuerySeparation.html ๏ https://en.wikipedia.org/wiki/Command%E2%80%93query_separation ๏ Bertrand Meyer: ๏ https://de.wikipedia.org/wiki/Bertrand_Meyer ๏ https://bertrandmeyer.com ๏ Book "Object-Oriented Software Construction 2Ed" by Bertrand Meyer: http://amzn.to/2qdnc2i ๏ Greg Young’s CQRS documents: https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf ๏ GET/HEAD methods: https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html ๏ Article about Event Sourcing by Martin Fowler: https://martinfowler.com/eaaDev/EventSourcing.html ๏ PHP FastCGI Client: https://github.com/hollodotme/fast-cgi-client