Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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 tries to show you examples for these benefits and how to give your application a memory.

Presented at PhpPrague Conference, June 23, 2018

Holger Woltersdorf

June 23, 2018
Tweet

More Decks by Holger Woltersdorf

Other Decks in Programming

Transcript

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

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 1 THE APPLICATION THAT NEVER FORGETS PARALELNÍ POLIS PRAGUE, CZECH REPUBLIC JUNE 23, 2018
  2. HOLGER WOLTERSDORF CIO • FATHER • HUSBAND • PHP DEV

    WITH ♥ github.com/hollodotme @hollodotme
  3. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 3 CQRS (COMMAND QUERY RESPONSIBILITY SEGREGATION)
  4. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF CQS => CQRS 4 ๏ 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
  5. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF CQS => CQRS 5 <?php declare(strict_types=1); class Number { /** @var int */ private $number = 0; public function getIncrementedNumber() : int { $this->number++; return $this->number; } } BAD
  6. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF CQS => CQRS 6 <?php declare(strict_types=1); class Number { /** @var int */ private $number = 0; public function getNumber() : int # Query { return $this->number; } public function increment() : void # Command { $this->number++; } } GOOD
  7. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

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

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 8 CQRS DEMO
  9. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 9 THREE 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
  10. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 10 THREE APPLICATION LAYERS READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE AFTER CQRS WAS APPLIED VIEW LOGIC
  11. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 11 THREE APPLICATION LAYERS READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE HOW DO WE GET FROM HERE… … TO THERE? VIEW LOGIC
  12. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 12 THREE APPLICATION LAYERS READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE PUBLISH SUBSCRIBE REDIRECT (+ SESSION) VIEW LOGIC
  13. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF REPRESENTATION 13 THREE APPLICATION LAYERS READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE VIEW LOGIC PUBLISH SUBSCRIBE REDIRECT (+ SESSION) REPRESENTATION MESSAGE BROKER ASYNC EVENTUAL CONSISTENCY !
  14. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF REPRESENTATION 14 THREE APPLICATION LAYERS READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE VIEW LOGIC PUBLISH SUBSCRIBE REDIRECT (+ SESSION) REPRESENTATION PHP FASTCGI CLIENT ASYNC EVENTUAL CONSISTENCY !
  15. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 15 CQRS (REQUEST HANDLING LAYER) ๏ GET-REQUEST ๏ NO VIEW LOGIC ๏ COULD BE A STATIC
 HTML PAGE ๏ SUBMIT TRIGGERS
 POST REQUEST
  16. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 16 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
  17. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF CQRS (REQUEST HANDLING LAYER) 17 class POSTRequestHandler { public function handle( $request, $session ) : void { #... $validator->checkUserInput( $request ); if ( $validator->failed() ) { $session->addErrors( $validator->getErrors() ); (new Redirect())->respond( '/newsletter-form' ); return; } #... } }
  18. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF CQRS (REQUEST HANDLING LAYER) 18 class GETRequestHandler { public function handle( $request, $session ) : void { $page = new TemplatePage('/newsletter-form'); if ( $session->hasErrors() ) { $page->addErrors( $session->getErrors() ); } $page->display(); } }
  19. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 19 CQRS (REQUEST HANDLING LAYER) ๏ GET-REQUEST ๏ NO VIEW LOGIC ๏ COULD BE A STATIC
 HTML PAGE ๏ CAN BE DELETED 
 AFTER SUBSCRIPTION
 IS FINISHED
  20. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF CQRS (REQUEST HANDLING LAYER) 20 class POSTRequestHandler { public function handle( $request, $session ) : void { #... input validation passed ... $command = new InitSubscriptionCommand( new Fullname( $request->get( 'fullname' ) ), new Email( $request->get( 'email' ) ) ); $handler = new InitSubscriptionCommandHandler(); $result = $handler->handle( $command ); if ( $result->succeeded() ) { # $publisher->publish( $result ); (new Redirect())->respond( '/success-' . $result->getSubscription()->getId() ); return; } } }
  21. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF CQRS (REQUEST HANDLING LAYER) 21 class InitSubscriptionResult { public function succeeded() : bool public function getErrors() : array public function getSubscription() : NewsletterSubscription } class Subscriber { public function notify( InitSubscriptionResult $result ) : void { $subscription = $result->getSubscription(); $page = new TemplatePage('/newsletter-init-success'); $page->assign( 'subscription', $subscription ); $page->saveAsHTML( '/newsletter-' . $subscription->getId() ); } }
  22. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 22 CQRS (REQUEST HANDLING LAYER) ๏ E-MAIL ๏ CALL TO ACTION
 FOR A STATE CHANGE ๏ POST REQUESTS
 NOT AVAILABLE 
 FROM E-MAILS!
  23. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 23 RESPECT HTTP VERBS! CQRS (REQUEST HANDLING LAYER)
  24. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 24 […] 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)
  25. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 25 CQRS (REQUEST HANDLING LAYER) ๏ DON’T CHANGE STATE FROM GET 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
  26. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 26 CQRS (REQUEST HANDLING LAYER) ๏ GET-REQUEST
 AFTER REDIRECT ๏ NO VIEW LOGIC ๏ COULD BE A STATIC
 HTML PAGE, PREPARED BASED ON SUCCESSFUL 
 FINISH RESULT
  27. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 27 BTW ;-) icehawk.github.io PHP7 micro framework - respecting CQRS
  28. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 28 PERSISTENCE LAYER & STATE SubscriberID Fullname Email Interval Subscribed Confirmed Unsubscribed ‹UUID-HW› Holger Woltersdorf hollodotme @PHPkonf.org ONLY WHEN PHPkonf happens 2017-05-19 2017-05-20 2017-05-21 ‹UUID-JD› Jane Doe [email protected] Monthly 2017-03-01 NULL NULL SubscriberID Topic ‹UUID-HW› HIPSTER JEANS ‹UUID-HW› NERD CAPS ‹UUID-HW› MEME SHIRTS
  29. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 29 PERSISTENCE LAYER & STATE WHAT HAPPENS, IF… I subscribe again for MEME SHIRTS only with WEEKLY interval
  30. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 30 PERSISTENCE LAYER & STATE SubscriberID Fullname Email Interval Subscribed Confirmed Unsubscribed ‹UUID-HW› Holger Woltersdorf hollodotme @PHPkonf.org Weekly 2017-05-22 2017-05-23 NULL ‹UUID-JD› Jane Doe [email protected] Monthly 2017-03-01 NULL NULL SubscriberID Topic ‹UUID-HW› MEME SHIRTS
  31. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 31 PERSISTENCE LAYER & STATE ‹‹ MY E-MAIL DUPLICATE CHECK WORKED OUT! ›› DEVELOPER: YES! ADMIN: YES! ‹‹ YOU KEPT OUR DATABASE SLIGHT AND FAST! ››
  32. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF ‹‹ ARE YOU INSANE? YOU JUST ERASED VALUABLE B.I. DATA! ›› 32 PERSISTENCE LAYER & STATE BUSINESS ANALYST:
  33. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF CQRS (PERSISTENCE LAYER - STATE) 33 What have we lost? ACTUALLY ALL INFORMATION ABOUT THE FIRST SUBSCRIPTION except e-mail & full name
  34. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF CQRS (PERSISTENCE LAYER - STATE) 34 What else have we lost? ANALYSABLE DATA ABOUT THE USER’S DECISION TO REACTIVATE AND CHANGE SUBSCRIPTION
  35. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 35 CQRS (PERSISTENCE LAYER - STATE) The application state is observable, BUT ONLY AT THE EXACT POINT OF TIME we look at it.
  36. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 36 + EVENT SOURCING
  37. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF EVENT SOURCING 37 ๏ 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
  38. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 38 INSTALLING EVENT SOURCING READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE PUBLISH SUBSCRIBE REDIRECT (+ SESSION) VIEW LOGIC
  39. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 39 INSTALLING EVENT SOURCING READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE PUBLISH SUBSCRIBE REDIRECT (+ SESSION) VIEW LOGIC
  40. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 40 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
  41. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF EVENT SOURCING 41 BUSINESS ENTITY EVENTSTORE COMMAND TRIGGER CHANGES EMITS EVENTS (ALL NEW CHANGES) BUILT FROM EVENT STREAM (HISTORY OF CHANGES) PUBLISH NEWLY RECORDED EVENTS
  42. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 42 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 …
  43. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. 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 … 43 EVENT STORE SEQUENCE SORTING ALL EVENTS IN THE EVENT STORE (AUTO-INCREMENT)
  44. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. 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 … 44 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)
  45. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. 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 … 45 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
  46. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. 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 … 46 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
  47. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. 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 … 47 EVENT STORE FIRST SUBSCRIPTION PLEASE NOTE: GIVING BIRTH TO A NEW BUSINESS ENTITY IS A CHANGE TO APPLICATION STATE
  48. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. 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 … 48 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
  49. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF EVENT STORE PAYLOAD 49 { "SubscriptionID": "‹UUID-HW›", "Fullname": "Holger Woltersdorf", "Email": "[email protected]" } "Subscription was initialised" { "SubscriptionID": "‹UUID-HW›" } vs. "Subscription was reactivated"
  50. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF EVENT STORE PAYLOAD 50 DO NOT ADD PREVIOUS VALUES TO PAYLOAD IT SHOULD HAVE BEEN RECORDED BEFORE STOP THINKING RELATIONAL START THINKING TEMPORAL
  51. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF EVENT SOURCING 51 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 }
  52. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF EVENT SOURCING 52 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…() : … }
  53. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF EVENT SOURCING 53 ๏ 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
  54. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF EVENT SOURCING (BUSINESS ENTITY) 54 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(); } # ... }
  55. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF EVENT SOURCING (BUSINESS ENTITY) 55 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
  56. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF EVENT SOURCING (BUSINESS ENTITY) 56 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 ); } #... }
  57. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF EVENT SOURCING (BUSINESS ENTITY) 57 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 ); } #... }
  58. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF EVENT SOURCING (BUSINESS ENTITY) 58 class NewsletterSubscription { #... protected function whenSubscriptionWasInitiated( SubscriptionWasInitiated $event ) : void { $this->email = $event->getEmail(); $this->fullname = $event->getFullname(); # This is your "real" constructor # Init properties here } #... }
  59. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF EVENT SOURCING (BUSINESS ENTITY) 59 class NewsletterSubscription { #... public static function reconstitute( EventStream $eventStream ) : self { $subscription = new self(); foreach ( $eventStream as $envelope ) { $subscription->apply( $envelope ); } return $subscription; } #... }
  60. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF EVENT SOURCING (BUSINESS ENTITY) 60 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 ) }
  61. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF EVENT SOURCING (BUSINESS ENTITY) 61 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 }
  62. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF EVENT SOURCING 62 ๏ 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?
  63. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 63 EVENT SOURCING READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS EVENT STORE PUBLISH SUBSCRIBE REDIRECT (+ SESSION) VIEW LOGIC READ HISTORY OF CHANGES RECORD NEW CHANGES
  64. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF RECORD NEW CHANGES READ HISTORY OF CHANGES 64 EVENT SOURCING READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS EVENT STORE PUBLISH SUBSCRIBE REDIRECT (+ SESSION) VIEW LOGIC
  65. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF RECORD NEW CHANGES READ HISTORY OF CHANGES 65 EVENT SOURCING (PROJECTION) READ REQUESTS BUSINESS LOGIC PROJECTION WRITE REQUESTS EVENT STORE PUBLISH SUBSCRIBE REDIRECT (+ SESSION) VIEW LOGIC
  66. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF RECORD NEW CHANGES READ HISTORY OF CHANGES 66 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 !
  67. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF EVENT SOURCING (PROJECTING) 67 ๏ 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
  68. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF EVENT SOURCING (PROJECTING) 68 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
  69. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF CQRS ADVANTAGES 69 ๏ 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
  70. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF EVENT SOURCING ADVANTAGES 70 ๏ 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 reestablish view consistency ๏ Easy backup
  71. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF 71 QUESTIONS?
  72. THANK YOU! github.com/hollodotme @hollodotme / @F9T3ch fortuneglobe.com phpug-dresden.org @phpugdd HOLGER

    WOLTERSDORF icehawk.github.io speakerdeck.com/hollodotme Slides available at:
  73. TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JUNE

    23rd 2018 • PHP PRAGUE CONFERENCE. HOLGER WOLTERSDORF LINKS / REFERENCES 73 ๏ 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 ๏ IceHawk micro framework: https://icehawk.github.io