Slide 1

Slide 1 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN HOLGER WOLTERSDORF 1 THE APPLICATION THAT NEVER FORGETS

Slide 2

Slide 2 text

HOLGER WOLTERSDORF CIO • FATHER • HUSBAND • PHP DEV WITH ♥ github.com/hollodotme @hollodotme

Slide 3

Slide 3 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN HOLGER WOLTERSDORF 3 CQRS (COMMAND QUERY RESPONSIBILITY SEGREGATION)

Slide 4

Slide 4 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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

Slide 5

Slide 5 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL HOLGER WOLTERSDORF CQS => CQRS 5 number++; return $this->number; } } BAD

Slide 6

Slide 6 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL HOLGER WOLTERSDORF CQS => CQRS 6 number; } public function increment() : void # Command { $this->number++; } } GOOD

Slide 7

Slide 7 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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

Slide 8

Slide 8 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN HOLGER WOLTERSDORF 8 CQRS DEMO

Slide 9

Slide 9 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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

Slide 10

Slide 10 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN HOLGER WOLTERSDORF 10 THREE APPLICATION LAYERS READ REQUESTS BUSINESS LOGIC REPRESENTATION WRITE REQUESTS STATE AFTER CQRS WAS APPLIED VIEW LOGIC

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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 !

Slide 14

Slide 14 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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 !

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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

Slide 17

Slide 17 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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; } #... } }

Slide 18

Slide 18 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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(); } }

Slide 19

Slide 19 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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

Slide 20

Slide 20 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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; } } }

Slide 21

Slide 21 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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() ); } }

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN HOLGER WOLTERSDORF 23 RESPECT HTTP VERBS! CQRS (REQUEST HANDLING LAYER)

Slide 24

Slide 24 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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)

Slide 25

Slide 25 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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

Slide 26

Slide 26 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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

Slide 27

Slide 27 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN HOLGER WOLTERSDORF 27 BTW ;-) icehawk.github.io PHP7 micro framework - respecting CQRS

Slide 28

Slide 28 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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

Slide 29

Slide 29 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL HOLGER WOLTERSDORF 29 PERSISTENCE LAYER & STATE WHAT HAPPENS, IF… I subscribe again for MEME SHIRTS only with WEEKLY interval

Slide 30

Slide 30 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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

Slide 31

Slide 31 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL HOLGER WOLTERSDORF 31 PERSISTENCE LAYER & STATE ‹‹ MY E-MAIL DUPLICATE CHECK WORKED OUT! ›› DEVELOPER: YES! ADMIN: YES! ‹‹ YOU KEPT OUR DATABASE SLIGHT AND FAST! ››

Slide 32

Slide 32 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL HOLGER WOLTERSDORF ‹‹ ARE YOU INSANE? YOU JUST ERASED VALUABLE B.I. DATA! ›› 32 PERSISTENCE LAYER & STATE BUSINESS ANALYST:

Slide 33

Slide 33 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL HOLGER WOLTERSDORF CQRS (PERSISTENCE LAYER - STATE) 33 What have we lost? ACTUALLY ALL INFORMATION ABOUT THE FIRST SUBSCRIPTION except e-mail & full name

Slide 34

Slide 34 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL HOLGER WOLTERSDORF CQRS (PERSISTENCE LAYER - STATE) 34 What else have we lost? ANALYSABLE DATA ABOUT THE USER’S DECISION TO REACTIVATE AND CHANGE SUBSCRIPTION

Slide 35

Slide 35 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL HOLGER WOLTERSDORF 35 CQRS (PERSISTENCE LAYER - STATE) The application state is observable, BUT ONLY AT THE EXACT POINT OF TIME we look at it.

Slide 36

Slide 36 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN HOLGER WOLTERSDORF 36 + EVENT SOURCING

Slide 37

Slide 37 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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

Slide 41

Slide 41 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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

Slide 42

Slide 42 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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 …

Slide 43

Slide 43 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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)

Slide 44

Slide 44 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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)

Slide 45

Slide 45 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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

Slide 46

Slide 46 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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

Slide 47

Slide 47 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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

Slide 48

Slide 48 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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

Slide 49

Slide 49 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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"

Slide 50

Slide 50 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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

Slide 51

Slide 51 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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 }

Slide 52

Slide 52 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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…() : … }

Slide 53

Slide 53 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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

Slide 54

Slide 54 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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(); } # ... }

Slide 55

Slide 55 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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

Slide 56

Slide 56 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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 ); } #... }

Slide 57

Slide 57 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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 ); } #... }

Slide 58

Slide 58 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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 } #... }

Slide 59

Slide 59 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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; } #... }

Slide 60

Slide 60 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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 ) }

Slide 61

Slide 61 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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 }

Slide 62

Slide 62 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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?

Slide 63

Slide 63 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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

Slide 64

Slide 64 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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

Slide 65

Slide 65 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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

Slide 66

Slide 66 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN 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 !

Slide 67

Slide 67 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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

Slide 68

Slide 68 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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

Slide 69

Slide 69 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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

Slide 70

Slide 70 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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

Slide 71

Slide 71 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • JAN 23rd 2018 • PHPUGDD. • DRESDEN HOLGER WOLTERSDORF 71 QUESTIONS?

Slide 72

Slide 72 text

THANK YOU! github.com/hollodotme @hollodotme / @F9T3ch fortuneglobe.com phpug-dresden.org @phpugdd HOLGER WOLTERSDORF icehawk.github.io speakerdeck.com/hollodotme Slides available at:

Slide 73

Slide 73 text

TOTAL RECALL - THE APPLICATION THAT NEVER FORGETS • MAY 20th 2017 • PHPkonf. • ISTANBUL 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