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

CQRS, or did you mean CARS?

CQRS, or did you mean CARS?

Everything you would want to discover when you're googling for CQRS for the first time. From general OOP principles to CQRS and Event Sourcing

Stijn Vannieuwenhuyse

February 25, 2014
Tweet

More Decks by Stijn Vannieuwenhuyse

Other Decks in Programming

Transcript

  1. SOME REMARKS I know where I'm heading I don't know

    where I'll arrive I have assumptions about your knowledge I want to know if my assumptions were correct I try to answer every question I want to be stopped when something is not clear I want to be stopped when it has been long enough I want to know if you don't agree I know the examples are over-simplified I want to know if there are mistakes in my slides ;-)
  2. CQRS COMMAND QUERY RESPONSIBILITY SEGREGATION split up your model in

    separate classes for either returning state or either changing state similar to CQS, but applied to classes design principle
  3. i n t e r f a c e S

    h o p p i n g C a r t { / / p u b l i c f u n c t i o n _ _ c o n s t r u c t ( $ s h o p p i n g C a r t I d ) ; p u b l i c f u n c t i o n a d d I t e m ( I t e m $ i t e m ) ; p u b l i c f u n c t i o n r e m o v e I t e m ( I t e m $ i t e m ) ; p u b l i c f u n c t i o n c h e c k o u t ( ) ; p u b l i c f u n c t i o n g e t I d ( ) ; p u b l i c f u n c t i o n g e t I t e m s ( ) ; p u b l i c f u n c t i o n g e t T o t a l ( ) ; }
  4. i n t e r f a c e S

    h o p p i n g C a r t R e p o s i t o r y { p u b l i c f u n c t i o n f i n d A l l ( ) ; p u b l i c f u n c t i o n f i n d O n e B y I d ( $ s h o p p i n g C a r t I d ) ; p u b l i c f u n c t i o n s a v e ( S h o p p i n g C a r t $ s h o p p i n g C a r t ) ; }
  5. c l a s s S h o p p

    i n g C a r t A p i C o n t r o l l e r { p u b l i c f u n c t i o n a d d I t e m T o S h o p p i n g C a r t ( $ s h o p p i n g C a r t I d , $ i t e m I d ) { $ s h o p p i n g C a r t = $ t h i s - > s h o p p i n g C a r t R e p o s i t o r y - > f i n d O n B y I d ( $ s h o p p i n g C a r t I d ) ; $ i t e m = $ t h i s - > i t e m R e p o s i t o r y - > f i n d O n e B y I d ( $ i t e m I d ) ; $ s h o p p i n g C a r t - > a d d I t e m ( $ i t e m ) ; $ t h i s - > s h o p p i n g C a r t R e p o s i t o r y - > s a v e ( $ s h o p p i n g C a r t ) ; } p u b l i c f u n c t i o n g e t S h o p p i n g C a r t I n f o ( $ s h o p p i n g C a r t I d ) { $ s h o p p i n g C a r t = $ t h i s - > s h o p p i n g C a r t R e p o s i t o r y - > f i n d O n B y I d ( $ s h o p p i n g C a r t I d ) ; r e t u r n j s o n _ e n c o d e ( ( o b j e c t ) [ ' i d ' = > $ s h o p p i n g C a r t - > g e t I d ( ) , ' n u m b e r O f I t e m s ' = > c o u n t ( $ s h o p p i n g C a r t - > g e t I t e m s ( ) ) , ' t o t a l ' = > $ s h o p p i n g C a r t - > g e t T o t a l ( ) ] ) ; } / / . . . }
  6. CQRS SPLIT YOUR MODEL split up your DTOs into read-entities

    and commands make a separate repository for your read-entities you are able to use your entities for write only you are able to use your read-entities for read only
  7. i n t e r f a c e S

    h o p p i n g C a r t { / / p u b l i c f u n c t i o n _ _ c o n s t r u c t ( $ s h o p p i n g C a r t I d ) ; p u b l i c f u n c t i o n a d d I t e m ( I t e m $ i t e m ) ; p u b l i c f u n c t i o n r e m o v e I t e m ( I t e m $ i t e m ) ; p u b l i c f u n c t i o n c h e c k o u t ( ) ; }
  8. i n t e r f a c e S

    h o p p i n g C a r t R e p o s i t o r y { p u b l i c f u n c t i o n f i n d O n e B y I d ( $ s h o p p i n g C a r t I d ) ; p u b l i c f u n c t i o n s a v e ( S h o p p i n g C a r t $ s h o p p i n g C a r t ) ; }
  9. c l a s s A d d I t

    e m T o S h o p p i n g C a r t i m p l e m e n t s C o m m a n d { p r i v a t e $ s h o p p i n g C a r t I d ; p r i v a t e $ i t e m I d ; p u b l i c f u n c t i o n _ _ c o n s t r u c t ( $ s h o p p i n g C a r t I d , $ i t e m I d ) { $ t h i s - > s h o p p i n g C a r t I d = $ s h o p p i n g C a r t I d ; $ t h i s - > i t e m I d = $ i t e m I d ; } p u b l i c f u n c t i o n g e t S h o p p i n g C a r t I d ( ) { r e t u r n $ t h i s - > s h o p p i n g C a r t I d ; } p u b l i c f u n c t i o n g e t I t e m I d ( ) { r e t u r n $ t h i s - > i t e m I d ; } }
  10. c l a s s A d d I t

    e m T o S h o p p i n g C a r t C o m m a n d H a n d l e r i m p l e m e n t s C o m m a n d H a n d l e r { p u b l i c f u n c t i o n h a n d l e ( A d d I t e m T o S h o p p i n g C a r t $ c o m m a n d ) { $ s h o p p i n g C a r t = $ t h i s - > s h o p p i n g C a r t R e p o s i t o r y - > f i n d O n B y I d ( $ c o m m a n d - > g e t S h o p p i n g C a r t I d ( ) ) ; $ i t e m = $ t h i s - > i t e m R e p o s i t o r y - > f i n d O n e B y I d ( $ c o m m a n d - > g e t I t e m I d ( ) ) ; $ s h o p p i n g C a r t - > a d d I t e m ( $ i t e m ) ; $ t h i s - > s h o p p i n g C a r t R e p o s i t o r y - > s a v e ( $ s h o p p i n g C a r t ) ; } / / . . . }
  11. i n t e r f a c e S

    h o p p i n g C a r t I n f o { / / p u b l i c f u n c t i o n _ _ c o n s t r u c t ( $ s h o p p i n g C a r t I d , $ n u m b e r O f I t e m s , $ t o t a l ) ; p u b l i c f u n c t i o n g e t I d ( ) ; p u b l i c f u n c t i o n g e t N u m b e r O f I t e m s ( ) ; p u b l i c f u n c t i o n g e t T o t a l ( ) ; p u b l i c f u n c t i o n j s o n S e r i a l i z e ( ) ; }
  12. i n t e r f a c e S

    h o p p i n g C a r t I n f o R e p o s i t o r y { p u b l i c f u n c t i o n f i n d A l l ( ) ; p u b l i c f u n c t i o n f i n d O n e B y I d ( $ s h o p p i n g C a r t I d ) ; }
  13. c l a s s S q l S h

    o p p i n g C a r t I n f o R e p o s i t o r y i m p l e m e n t s S h o p p i n g C a r t I n f o R e p o s i t o r y { / / . . . p u b l i c f u n c t i o n f i n d O n e B y I d ( $ s h o p p i n g C a r t I d ) { $ s q l = ' S E L E C T . . . ' ; / / s o m e c o m p l e x s q l s t a t e m e n t $ r e s u l t = $ t h i s - > d a t a b a s e - > f e t c h ( $ s q l ) ; r e t u r n n e w S h o p p i n g C a r t I n f o ( $ r e s u l t - > i d , $ r e s u l t - > n u m b e r O f I t e m s , $ r e s u l t - > t o t a l ) ; } }
  14. c l a s s S h o p p

    i n g C a r t A p i C o n t r o l l e r { / / . . . p u b l i c f u n c t i o n g e t S h o p p i n g C a r t I n f o ( $ s h o p p i n g C a r t I d ) { r e t u r n j s o n _ e n c o d e ( $ t h i s - > s h o p p i n g C a r t I n f o R e p o s i t o r y - > f i n d O n B y I d ( $ s h o p p i n g C a r t I d ) ) ; } }
  15. c l a s s C o m m a

    n d A p i C o n t r o l l e r { / / . . . p u b l i c f u n c t i o n h a n d l e ( $ c o m m a n d ) { $ c o m m a n d = $ t h i s - > c o m m a n d D e s e r i a l i z e r - > d e s e r i a l i z e ( $ c o m m a n d ) ; $ t h i s - > c o m m a n d D i s p a t c h e r - > d i s p a t c h ( $ c o m m a n d ) ; } }
  16. DESIGN move logic to your model instead of application layer

    make domain language explicit in your code
  17. PROGRAMMER OPTIMIZATION let senior developers work the write side let

    junior developers work the read side let front-end developers work the client side or not...
  18. WRITE MODEL DB CLIENT command orm persists sql resultset dto

    READ MODEL COMMAND HANDLER DTO BUILDER
  19. CQRS COMMAND QUERY RESPONSIBILITY SEGREGATION split up your persistence in

    separate parts for either returning state or either changing state architectural choice
  20. WRITE MODEL DB CLIENT command orm persists sql resultset dto

    READ MODEL COMMAND HANDLER DTO BUILDER DB
  21. WRITE MODEL DB CLIENT command orm persists sql resultset dto

    READ MODEL COMMAND HANDLER DTO BUILDER DB event
  22. c l a s s I t e m W

    a s A d d e d T o S h o p p i n g C a r t i m p l e m e n t s D o m a i n E v e n t { p r i v a t e $ s h o p p i n g C a r t I d ; p r i v a t e $ i t e m I d ; p r i v a t e $ p r i c e ; p u b l i c f u n c t i o n _ _ c o n s t r u c t ( $ s h o p p i n g C a r t I d , $ i t e m I d , $ p r i c e ) { $ t h i s - > s h o p p i n g C a r t I d = $ s h o p p i n g C a r t I d ; $ t h i s - > i t e m I d = $ i t e m I d ; $ t h i s - > p r i c e = $ p r i c e ; } p u b l i c f u n c t i o n g e t S h o p p i n g C a r t I d ( ) { r e t u r n $ t h i s - > s h o p p i n g C a r t I d ; } p u b l i c f u n c t i o n g e t I t e m I d ( ) { r e t u r n $ t h i s - > i t e m I d ; } p u b l i c f u n c t i o n g e t P r i c e ( ) { r e t u r n $ t h i s - > p r i c e ; } }
  23. COMMAND HANDLER turns a command into events uses an aggregate

    to protect invariants publishes events
  24. c l a s s A d d I t

    e m T o S h o p p i n g C a r t C o m m a n d H a n d l e r i m p l e m e n t s C o m m a n d H a n d l e r { p u b l i c f u n c t i o n h a n d l e ( A d d I t e m T o S h o p p i n g C a r t $ c o m m a n d ) { $ s h o p p i n g C a r t = $ t h i s - > s h o p p i n g C a r t R e p o s i t o r y - > f i n d O n B y I d ( $ c o m m a n d - > g e t S h o p p i n g C a r t I d ( ) ) ; $ i t e m = $ t h i s - > i t e m R e p o s i t o r y - > f i n d O n e B y I d ( $ c o m m a n d - > g e t I t e m I d ( ) ) ; $ s h o p p i n g C a r t - > a d d I t e m ( $ i t e m , $ t h i s - > e v e n t P u b l i s h e r ) ; $ t h i s - > s h o p p i n g C a r t R e p o s i t o r y - > s a v e ( $ s h o p p i n g C a r t ) ; } / / . . . }
  25. c l a s s S h o p p

    i n g C a r t i m p l e m e n t s A g g r e g a t e R o o t { p u b l i c f u n c t i o n a d d I t e m ( I t e m $ i t e m , E v e n t P u b l i s h e r $ e v e n t P u b l i s h e r ) { $ t h i s - > i t e m s [ ] = $ i t e m ; $ e v e n t P u b l i s h e r - > p u b l i s h ( n e w I t e m W a s A d d e d T o S h o p p i n g C a r t ( $ t h i s - > g e t I d ( ) , $ i t e m - > g e t I d ( ) , $ i t e m - > g e t P r i c e ( ) ) ) ; } / / . . . }
  26. c l a s s S h o p p

    i n g C a r t I n f o P r o j e c t o r i m p l e m e n t s P r o j e c t o r { p u b l i c f u n c t i o n p r o j e c t ( I t e m W a s A d d e d T o S h o p p i n g C a r t $ e v e n t ) { $ s h o p p i n g C a r t I n f o = $ t h i s - > s h o p p i n g C a r t I n f o R e p o s i t o r y - > f i n d O n B y I d ( $ e v e n t - > g e t S h o p p i n g C a r t I d ( ) ) ; $ s h o p p i n g C a r t I n f o - > u p d a t e ( 1 , $ e v e n t - > g e t P r i c e ( ) ) ; $ t h i s - > s h o p p i n g C a r t I n f o R e p o s i t o r y - > s a v e ( $ s h o p p i n g C a r t I n f o ) ; } / / . . . }
  27. READ MODEL READ MODEL WRITE MODEL DB CLIENT command orm

    persists dto READ MODEL COMMAND HANDLER DTO BUILDER RDB event KVS DDB
  28. BTW EVENTSTORMING workshop format to detect commands & events of

    the business with domain experts introduced by Alberto Brandolini
  29. READ MODEL READ MODEL WRITE MODEL DB CLIENT command orm

    persists dto READ MODEL COMMAND HANDLER DTO BUILDER RDB event KVS DDB
  30. EVENT SOURCING all events produce the current state all events

    of an aggregate produce the current state of the aggregate use events as persistence instead of state
  31. READ MODEL READ MODEL WRITE MODEL ES CLIENT command event

    dto READ MODEL COMMAND HANDLER DTO BUILDER RDB event KVS DDB
  32. CHANGING STATE an aggregate records its own new events newly

    recorded events are appended to an event store
  33. c l a s s S h o p p

    i n g C a r t { p u b l i c f u n c t i o n a d d I t e m ( $ i t e m I d , $ p r i c e ) { $ t h i s - > c h a n g e s [ ] = n e w I t e m W a s A d d e d T o S h o p p i n g C a r t ( $ t h i s - > s h o p p i n g C a r t I d , $ i t e m I d , $ p r i c e ) ; } / / . . . }
  34. c l a s s A d d I t

    e m T o S h o p p i n g C a r t C o m m a n d H a n d l e r i m p l e m e n t s C o m m a n d H a n d l e r { p u b l i c f u n c t i o n h a n d l e ( A d d I t e m T o S h o p p i n g C a r t $ c o m m a n d ) { $ s h o p p i n g C a r t = $ t h i s - > s h o p p i n g C a r t R e p o s i t o r y - > f i n d O n B y I d ( $ c o m m a n d - > g e t S h o p p i n g C a r t I d ( ) ) ; $ i t e m = $ t h i s - > i t e m R e p o s i t o r y - > f i n d O n e B y I d ( $ c o m m a n d - > g e t I t e m I d ( ) ) ; $ s h o p p i n g C a r t - > a d d I t e m ( $ i t e m - > g e t I d ( ) , $ i t e m - > g e t P r i c e ( ) ) ; $ t h i s - > e v e n t S t o r e - > c o m m i t ( $ s h o p p i n g C a r t - > g e t C h a n g e s ( ) ) ; } / / . . . }
  35. c l a s s E v e n t

    S t o r e S h o p p i n g C a r t R e p o s i t o r y i m p l e m e n t s S h o p p i n g C a r t R e p o s i t o r y { p u b l i c f u n c t i o n f i n d O n B y I d ( $ s h o p p i n g C a r t I d ) { $ s h o p p i n g C a r t E v e n t s = $ t h i s - > e v e n t S t o r e - > f i n d F o r ( $ s h o p p i n g C a r t I d ) ; r e t u r n S h o p p i n g C a r t : : r e c o n s t i t u t e ( $ s h o p p i n g C a r t I d , $ s h o p p i n g C a r t E v e n t s ) ; } / / . . . }
  36. INVARIANTS an aggregate needs history to protect invariants apply all

    events to the aggregate to rebuild relevant state
  37. c l a s s S h o p p

    i n g C a r t i m p l e m e n t s A g g r e g a t e R o o t { p u b l i c s t a t i c f u n c t i o n r e c o n s t i t u t e ( $ s h o p p i n g C a r t I d , a r r a y $ s h o p p i n g C a r t E v e n t s ) { $ s h o p p i n g C a r t = n e w s e l f ( $ s h o p p i n g C a r t I d ) ; f o r e a c h ( $ s h o p p i n g C a r t E v e n t s a s $ e v e n t ) { $ s h o p p i n g C a r t - > a p p l y ( $ e v e n t ) ; } r e t u r n $ s h o p p i n g C a r t ; } p r i v a t e f u n c t i o n a p p l y ( D o m a i n E v e n t $ e v e n t ) { / / f o r w a r d t o s p e c i f i c a p p l y - m e t h o d } p r i v a t e f u n c t i o n a p p l y I t e m W a s A d d e d T o S h o p p i n g C a r t ( I t e m W a s A d d e d T o S h o p p i n g C a r t $ e v e n t ) { } / / . . . }
  38. c l a s s S h o p p

    i n g C a r t i m p l e m e n t s A g g r e g a t e R o o t { p r i v a t e $ n u m b e r O f I t e m s = 0 ; p u b l i c f u n c t i o n a d d I t e m ( $ i t e m I d , $ p r i c e ) { i f ( $ t h i s - > n u m b e r O f I t e m s > 2 ) { t h r o w n e w E x c e p t i o n ( ' Y o u c a n o n l y h a v e 3 i t e m s i n y o u r s h o p p i n g c a r t ' ) ; } $ e v e n t = n e w I t e m W a s A d d e d T o S h o p p i n g C a r t ( $ t h i s - > s h o p p i n g C a r t I d , $ i t e m I d , $ p r i c e ) ; $ t h i s - > c h a n g e s [ ] = $ e v e n t ; $ t h i s - > a p p l y ( $ e v e n t ) ; } p r i v a t e f u n c t i o n a p p l y I t e m W a s A d d e d T o S h o p p i n g C a r t ( I t e m W a s A d d e d T o S h o p p i n g C a r t $ e v e n t ) { $ t h i s - > n u m b e r O f I t e m s + + ; } }
  39. TESTABILITY given a history, when I act upon the system,

    then something should have changed given certain events, when I issue a command, then events should have happened given a history, how is it represented given certain events, then a certain state should be present
  40. c l a s s A d d I t

    e m T o S h o p p i n g C a r t C o m m a n d H a n d l e r T e s t e x t e n d s T e s t C a s e { / * * @ t e s t * / p u b l i c f u n c t i o n s h o u l d _ r e s u l t _ i n t o _ a n _ I t e m W a s A d d e d T o S h o p p i n g C a r t _ e v e n t ( ) { $ t h i s - > s c e n a r i o - > g i v e n ( n e w S h o p p i n g C a r t W a s C r e a t e d ( / * . . . * / ) ) ; $ t h i s - > s c e n a r i o - > w h e n ( n e w A d d I t e m T o S h o p p i n g C a r t ( / * . . . * / ) ) ; $ t h i s - > s c e n a r i o - > t h e n ( n e w I t e m W a s A d d e d T o S h o p p i n g C a r t ( / * . . . * / ) ) ; } / * * @ t e s t * / p u b l i c f u n c t i o n s h o u l d _ t h r o w _ w h e n _ t r y i n g _ t o _ a d d _ m o r e _ t h e n _ t h r e e _ i t e m s ( ) { $ t h i s - > s c e n a r i o - > g i v e n ( n e w S h o p p i n g C a r t W a s C r e a t e d ( / * . . . * / ) ) ; $ t h i s - > s c e n a r i o - > g i v e n ( n e w I t e m W a s A d d e d T o S h o p p i n g C a r t ( / * . . . * / ) ) ; $ t h i s - > s c e n a r i o - > g i v e n ( n e w I t e m W a s A d d e d T o S h o p p i n g C a r t ( / * . . . * / ) ) ; $ t h i s - > s c e n a r i o - > g i v e n ( n e w I t e m W a s A d d e d T o S h o p p i n g C a r t ( / * . . . * / ) ) ; $ t h i s - > s c e n a r i o - > w h e n ( n e w A d d I t e m T o S h o p p i n g C a r t ( / * . . . * / ) ) ; $ t h i s - > s c e n a r i o - > t h e n ( n e w E x c e p t i o n ( / * . . . * / ) ) ; } p u b l i c f u n c t i o n s e t U p ( ) { $ t h i s - > s c e n a r i o = n e w S c e n a r i o ( / * . . . * / ) ; } p u b l i c f u n c t i o n t e a r D o w n ( ) { $ t h i s - > s c e n a r i o - > v e r i f y ( ) ;
  41. } }

  42. c l a s s S h o p p

    i n g C a r t I n f o P r o j e c t o r T e s t e x t e n d s T e s t C a s e { / * * @ t e s t * / p u b l i c f u n c t i o n s h o u l d _ p r o j e c t _ a n _ I t e m W a s A d d e d T o S h o p p i n g C a r t _ e v e n t ( ) { $ t h i s - > s c e n a r i o - > g i v e n ( n e w S h o p p i n g C a r t W a s C r e a t e d ( / * . . . * / ) ) ; $ t h i s - > s c e n a r i o - > g i v e n ( n e w I t e m W a s A d d e d T o S h o p p i n g C a r t ( / * . . . * / ) ) ; $ t h i s - > s c e n a r i o - > t h e n ( n e w S h o p p i n g C a r t I n f o ( / * . . * / ) ) ; } / / . . . }
  43. IMMUTABILITY an event store is append only, no database migrations

    needed messages are immutable, event versioning is needed tests don't need adaptation
  44. CHANGING REQUIREMENTS read-models are temporary, you can throw them away

    don't change read-models, just make a new one add commands and events as needed
  45. READ MODEL READ MODEL WRITE MODEL ES CLIENT command event

    dto READ MODEL COMMAND HANDLER DTO BUILDER RDB event KVS DDB
  46. READ MODEL READ MODEL WRITE MODEL ES CLIENT command event

    dto READ MODEL COMMAND HANDLER DTO BUILDER RDB event KVS DDB MQ
  47. READ MODEL READ MODEL WRITE MODEL ES CLIENT command event

    query READ MODEL COMMAND HANDLER DTO BUILDER RDB event KVS DDB MQ
  48. EVENTUAL CONSISTENCY clients get immediate ACK-NACK projectors receive events asynchronously

    read-models are not always up to date read-models are always valid
  49. READ MODEL READ MODEL WRITE MODEL ES CLIENT command event

    dto READ MODEL COMMAND HANDLER DTO BUILDER RDB event KVS DDB MQ
  50. CQRS COMMAND QUERY RESPONSIBILITY SEGREGATION Single Responsiblity Principle applied on

    class level Single Responsiblity Principle applied on architectural level not an architecture not a framework not a library not event sourcing
  51. USEFULL RESOURCES @gregyoung - CQRS guru @ericevans0 - DDD guru

    @martinfowler - EAA guru @mathiasverraes - DDD & PHP guru @ziobrando - DDD & eventstorming guru @DDDBE - DDD & CQRS gurus DDD/CQRS - https://groups.google.com/forum/#!forum/dddcqrs DDDinPHP - https://groups.google.com/forum/#!forum/dddinphp