divide and conquer - 2 years of cqrs in production
TRANSCRIPT
DIVIDEANDCONQUER2 YEARS OF CQRS IN PRODUCTION
30. SEPTEMBER • CODE.TALKS 2016 • HAMBURG
HOLGER WOLTERSDORF
GRÜNDUNGS-MITGLIED DER
phpind.de
HOLGER WOLTERSDORF CIO • FATHER • HUSBAND • PHP DEV WITH ♥
github.com/hollodotme github.com/PHPinDD
@hollodotme @phpindd
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
KLEINE ZEITREISE
3
2014 20162015
MVC vs. CQRS
EINFLÜSSE & GRUNDSÄTZE
Framework 1.0 Framework 2.0
Pitfalls
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
MVC vs. CQRS
5
MODEL VIEW
CONTROLLER (MVC)
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
MVC vs. CQRS
6
๏ WEB-MVC FAQ [fʌk]
๏ Wie viel Logik gehört ins Model?
๏ Ist das Template meine View?
๏ Was kontrolliert ein Controller?
๏ … und was sind eigentlich Helper?
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
MVC vs. CQRS
7
(eine nicht gänzlich unbekannte PHP Beratungsfirma)
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
MVC vs. CQRS
8
๏ DAS MVC PATTERN
VIEW VIEW
CONTROLLER MODEL
BENUTZER EINGABE
MODIFIZIERT
AKTUALISIERT
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
MVC vs. CQRS
9
๏ DAS MVC PATTERN
VIEW VIEW
CONTROLLER MODEL
BENUTZER EINGABE
MODIFIZIERT
AKTUALISIERT!
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
MVC vs. CQRS
10
HTTP ZIEHT EINE NATÜRLICHE GRENZE
ZWISCHEN SERVER UND CLIENT
(…ja, ja Web-Sockets)
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
MVC vs. CQRS
11
DAS MVC-PATTERN LÖST EIN PROBLEM, DAS ES IM WEB
NICHT GIBT.
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
MVC vs. CQRS
12
๏ NEUE FRAGEN
๏ Warum nicht Lesen und Schreiben trennen?
๏ Wird ein Request kontrolliert oder behandelt?
๏ Wir liefern doch Seiten aus, warum gibt es keine Klasse, die Page heißt?
๏ Sollten Schreibvorgänge Ausgabe produzieren?
๏ Was ist ein Framework?
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
MVC vs. CQRS
13
COMMAND QUERY
RESPONSIBILITY SEGREGATION
(CQRS)
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
MVC vs. CQRS
14
๏ DAS CQRS PATTERN (respektiert HTTP)
PAGE 1 PAGE 2
REQUESTHANDLER APP STATE
BENUTZER EINGABE
MODIFIZIERT
VORBEREITENPOST
CLIENT / BROWSER
REDIRECT ZU PAGE 2
GET GET
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
MVC vs. CQRS
15
๏ DAS CQRS PATTERN (respektiert HTTP)
PAGE 1 PAGE 2
REQUESTHANDLER APP STATE
BENUTZER EINGABE
MODIFIZIERT
VORBEREITENPOST
CLIENT / BROWSER
REDIRECT ZU PAGE 2
GET GET
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
MVC vs. CQRS
16
๏ CQRS REGELN
๏ Ein POST-Request liefert keinen Content aus
๏ Ein POST-Request kann den App-Zustand ändern
๏ Ein POST-Request antwortet mit einem Redirect
๏ Ein GET-Request liefert Content aus
๏ Ein GET-Request darf nicht den App-Zustand ändern
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
MVC vs. CQRS
18
WAS IST DER UNTERSCHIED ZWISCHEN EINER (GUTEN) REST-API
UND EINER WEBSITE?
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
MVC vs. CQRS
19
๏ REST-API
๏ REST-API ändert Ressourcen durch Schreibbefehle (POST / PUT / PATCH / DELETE)
๏ REST-API verweist auf geänderte Ressourcen (z.B. durch HATEOAS*-Tags)
๏ REST-API liefert Ressourcen im aktuellen Zustand durch Leseanfragen (GET)
๏ REST-API liefert Information über das Vorhandensein von Ressourcen und möglichen Aktionen (HEAD / OPTIONS)
* HATEOAS = Hypermedia As The Engine Of Application State
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
MVC vs. CQRS
20
๏ WEBSITE
๏ Website ändert den App-State durch Schreibbefehle (POST [/ PUT / PATCH / DELETE])
๏ Website verweist auf geänderte Seiten (REDIRECT)
๏ Website liefert Seiten im aktuellen Zustand durch Leseanfragen (GET)
๏ Website liefert Information über das Vorhandensein von Seiten und möglichen Aktionen (HEAD / OPTIONS)
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
MVC vs. CQRS
21
๏ REST-API != WEBSITE
๏ Website hat einen "flüchtigen" Zustand (Session)
๏ Website liefert HTML statt XML/JSON/etc. aus
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
MVC vs. CQRS
22
SUPRISE, SUPRISE!
GUTE REST-APIs SETZEN AUF CQRS
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
MVC vs. CQRS
23
OKAY. UND WIE IMPLEMENTIERT
MAN DAS JETZT?
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
EINFLÜSSE & GRUNDSÄTZE
25
https://37signals.com/manifesto
Google Group DDDinPHP
http://DDDinPHP.org
Gestartet von
Mathias Verraes
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
EINFLÜSSE & GRUNDSÄTZE
26
๏ REWORK
๏ Schaffe Overhead ab
๏ Mach es selbst, bis es nicht mehr geht
๏ Entwickle Software für dein Business
๏ Sei konsequent und ehrlich
๏ Mach es so einfach wie möglich
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
EINFLÜSSE & GRUNDSÄTZE
27
๏ CLEAN CODE
๏ Namen und Lesbarkeit sind wichtig
๏ Inline-Kommentare sind redundanter Müll
๏ Kohäsion in Klassen
๏ SOLID Prinzipien
๏ Richtig testen & stetiges Refactoring
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
EINFLÜSSE & GRUNDSÄTZE
28
๏ GOOGLE GROUP DDD IN PHP
๏ Konvention für Interface-, Class- und Trait-Namen
๏ Domains & Subdomains
๏ Commands & Queries
๏ Aggregates, Entities & Value Objects
๏ Event Sourcing für besseres Verständnis von CQRS
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
EINFLÜSSE & GRUNDSÄTZE
29
UNSERE 4 GRUNDSÄTZE
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
EINFLÜSSE & GRUNDSÄTZE
30
NO MAGIC$product = Rage::getModel('catalog/product');
# vs.
$product = new Catalog\Product();
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
EINFLÜSSE & GRUNDSÄTZE
31
WRITE MORE - DO LESS๏ Spalte Zuständigkeiten in (viele) Klassen auf
๏ Schreibe kurze & aussagekräftige Methoden
๏ Benutze Interfaces, Value-Objects und Type-Hints
๏ Teile keinen Business-Code in mehreren Projekten
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
EINFLÜSSE & GRUNDSÄTZE
32
USE (plain) PHP๏ Lerne PHPs Funktionsumfang zu nutzen
๏ Vermeide Drittanbieter-Libraries
๏ Verstehe das Problem - Löse es selbst
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
EINFLÜSSE & GRUNDSÄTZE
33
THINK ABOUT THE NEXT DEV๏ Schreibe unmissverständlichen, weisenden Code
๏ Vergebe sprechende (auch lange) Namen
๏ Schreibe (vollständige) Tests
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
EINFLÜSSE & GRUNDSÄTZE
34
NAMENS-KONVENTIONinterface LogsActivity {}
class ActivityLogger {}
trait ActivityLogging {}
class ActivityLogger implements LogsActivity { use ActivityLogging; }
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF35
«Wir brauchen einen Namen!»
«Ja, irgendwas cooles schnelles!»
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 1.0
38
๏ WAS IST EIN FRAMEWORK?
๏ Session- & Error-Handling initialisieren
๏ URIs auflösen & umschreiben
๏ Lese-Request-Daten weiter reichen
๏ Schreib-Request-Daten weiter reichen
๏ Requests behandeln
๏ Seiten ausliefern & Umleiten
๏ Finale Fehler-Behandlung (für 404 / 500 Seiten)
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 1.0
39
class IceHawk { public function __construct( $config, $delegate )
public function init() {}
public function handleRequest() {} }
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 1.0
40
REQUESTS & RESPONSES
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 1.0
41
class RequestInfo {}
class GetRequest {}
abstract class GetRequestHandler {}
class PostRequest {}
abstract class PostRequestHandler {}
REQUEST-VERARBEITUNG
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 1.0
42
class Page {}
class NotFound {}
class InternalServerError {}
class Redirect {}
RESPONSES
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 1.0
43
DELEGATION & KONFIGURATION
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 1.0
44
class IceHawkDelegate { public function setUpSessionHandling() {}
public function setUpErrorHandling() {}
public function handleUncaughtException( \Exception $exception ) {} }
DELEGATION
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 1.0
45
class IceHawkConfig { public function getRequestInfo() {}
public function getUriResolver() {}
public function getUriRewriter() {}
public function getDomainNamespace() {} }
KONFIGURATION
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 1.0
46
class UriResolver { public function resolveUri(RequestInfo $requestInfo) { # Löse die URI hier auf einen RequestHandler auf
return new UriComponents('Products', 'ShowGallery'); } }
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 1.0
47
class UriRewriter { public function rewriteUri(RequestInfo $requestInfo) { # Schreibe die URI hier um (new Redirect('/code/talks/2016'))->respond(); } }
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 1.0
48
class SessionRegistry {}
class FormData {}
class Command {}
class Query {}
class IceHawkWasInitializedEvent {}
class HandlingRequestEvent {}
class RequestWasHandledEvent {}
UND…
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 1.0
49
RELEASED 30.05.2015
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 1.0
50
PAGE 1 PAGE 2
PostRequestHandler MYSQL
BENUTZER EINGABE
COMMAND
QUERYPOST
CLIENT / BROWSER
REDIRECT
GET GET
GetRequestHandler GetRequestHandler
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 1.0
51
PAGE 1 PAGE 2
PostRequestHandler MYSQL
BENUTZER EINGABE
COMMAND
QUERYPOST
CLIENT / BROWSER
REDIRECT
GET GET
GetRequestHandler GetRequestHandler
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 1.0
52
PAGE 1 PAGE 2
PostRequestHandler MYSQL
BENUTZER EINGABE
COMMAND
QUERYPOST
CLIENT / BROWSER
REDIRECT
GET GET
GetRequestHandler GetRequestHandler
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 1.0
53
SCHNELLE AUTOS HABEN ZWEI ENDROHRE
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 1.0
54
PAGE 1 PAGE 2
PostRequestHandler MYSQL
BENUTZER EINGABE
COMMAND
QUERYPOST
CLIENT / BROWSER
REDIRECT
GET GET
GetRequestHandler GetRequestHandler
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 1.0
55
ETWAS ÜBER 1 JAHR UND…
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
PITFALLS
59
๏ OPT-IN E-MAILS
๏ RETURN-URLs (z.B. EXTERNE BEZAHLSYSTEM)
๏ AJAX
๏ CSRF TOKENS
๏ UNVOLLSTÄNDIGE READ/WRITE TRENNUNG
๏ UNFLEXIBLES ZUWEISEN VON REQUEST-HANDLERN …WEIL MAGIC
๏ DEFAULT-ROUTING
๏ VERLETZUNG DES SOC-PRINZIPS
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
PITFALLS
60
๏ OPT-IN E-MAILS
๏ Kein POST-Request möglich
๏ Option 1: Erneut ein Formular anzeigen
๏ Option 2: Die State-Änderung via GET-Request zulassen + REDIRECT
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
PITFALLS
61
๏ RETURN-URLs
๏ Kommen i.d.R. via GET-Request zurück
๏ Lösung: Die State-Änderung via GET-Request zulassen + REDIRECT
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
PITFALLS
62
๏ AJAX
๏ Redirects auf GET-Request funktionieren auch nach einem POST-XHR
๏ ALLES GUT
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
PITFALLS
63
๏ CSRF-TOKEN
๏ Gehört die Session zum App-State?
๏ Lösung: Nein. Die Session darf auch bei GET-Requests verändert werden.
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
PITFALLS
64
๏ UNVOLLSTÄNDIGE READ/WRITE TRENNUNG
๏ Trennung erfolgte erst nach dem Resolving
๏ Events unterschieden nicht nach Read/Write
๏ Finale Fehlerbehandlung unterschied nicht nach Read/Write
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
PITFALLS
65
๏ UNFLEXIBLES ZUWEISEN VON REQUEST-HANDLERN …WEIL MAGIC
๏ Suchpfad von Request-Handlern war je Projekt fix
๏ Beim Resolving war nicht ersichtlich, ob auf einen Read oder Write Handler aufgelöst wurde
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
PITFALLS
66
๏ DEFAULT-ROUTING
๏ Handler waren oft über 2 URIs erreichbar
๏ Lösung: Default-Routing abschaffen.
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
PITFALLS
67
๏ VERLETZUNG DES SOC-PRINZIPS
๏ Session-Wrapper gehörte nicht ins Framework
๏ FormData gehörte nicht ins Framework
๏ Command / Query gehörten nicht ins Framework
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 2.0
71
public function getReadRoutes() { return [ new ReadRoute( new Literal('/products/gallery'), new ProductGalleryRequestHandler() ), #... ]; }
NO MAGIC, REALLY
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 2.0
72
class FinalReadResponder { public function handleUncaughtException( \Throwable $throwable, ProvidesReadRequestData $request ); }
FINAL RESPONDING R/W
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 2.0
73
class InitializingIceHawkEvent {}
class IceHawkWasInitializedEvent {}
class HandlingReadRequestEvent {}
class ReadRequestWasHandledEvent {}
class HandlingWriteRequestEvent {}
class WriteRequestWasHandledEvent {}
EVENTS R/W
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 2.0
74
๏ WEITERE "FEATURES"
๏ Unterstützung aller HTTP-Methoden
๏ Interfaces definieren die erlaubten HTTP-Methoden
๏ Auto-Responding auf OPTIONS-Request
๏ "Traitful" Config (Defaults via Traits)
๏ Kein Default-Routing mehr
๏ Literal, RegExp und NamedRegExp URI-Patterns
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
ICEHAWK 2.0
75
๏ NEUE PACKAGES
๏ CSRF-Token Support und Feedback (Forms)
๏ Session Data Mapping (Session)
๏ Messaging (PubSub)
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF76
ICEHAWK FORMS
SESSION PUBSUB
COMING SOON
EVENTSTORE
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF77
BUILD PASSING COVERAGE 100% LICENSE MIT
RC5
phpind.de
VIELEN DANK!
github.com/hollodotme github.com/PHPinDD
@hollodotme / @phpindd
fortuneglobe.com
www.phpug-dresden.org @phpugdd
HOLGER WOLTERSDORF
https://joind.in/talk/f5095
slides available on slideshare
FEEDBACK WELCOME
github.com/icehawk
DIVIDE AND CONQUER • 2 YEARS OF CQRS IN PRODUCTION HOLGER WOLTERSDORF
LINKS / REFERENCES
81
๏ MVC-Pattern: http://martinfowler.com/eaaDev/uiArchs.html ๏ CQRS-Pattern: http://martinfowler.com/bliki/CQRS.html ๏ "Rework" by David Heinemeier Hansson: http://amzn.to/2cWOwxQ ๏ "Clean Code" by Robert C. Martin: http://amzn.to/2dthxQn ๏ Google-Group DDDinPHP: https://groups.google.com/forum/#!forum/dddinphp ๏ "Traitful" Configs: https://phpind.de/posts/traitful-configs
๏ IceHawk Framework on GitHub: https://github.com/icehawk