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

0cab45c75f109eb044564efbd28af603?s=128

Stijn Vannieuwenhuyse

February 25, 2014
Tweet

Transcript

  1. 3.

    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. 5.
  3. 6.
  4. 12.
  5. 22.

    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
  6. 24.

    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 ( ) ; }
  7. 25.

    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 ) ; }
  8. 26.

    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 ( ) ] ) ; } / / . . . }
  9. 27.

    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
  10. 28.

    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 ( ) ; }
  11. 29.

    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 ) ; }
  12. 30.

    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 ; } }
  13. 32.

    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 ) ; } / / . . . }
  14. 35.

    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 ( ) ; }
  15. 36.

    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 ) ; }
  16. 37.

    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 ) ; } }
  17. 38.

    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 ) ) ; } }
  18. 39.

    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 ) ; } }
  19. 40.

    DESIGN move logic to your model instead of application layer

    make domain language explicit in your code
  20. 41.

    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...
  21. 43.

    WRITE MODEL DB CLIENT command orm persists sql resultset dto

    READ MODEL COMMAND HANDLER DTO BUILDER
  22. 44.

    CQRS COMMAND QUERY RESPONSIBILITY SEGREGATION split up your persistence in

    separate parts for either returning state or either changing state architectural choice
  23. 45.

    WRITE MODEL DB CLIENT command orm persists sql resultset dto

    READ MODEL COMMAND HANDLER DTO BUILDER DB
  24. 46.

    WRITE MODEL DB CLIENT command orm persists sql resultset dto

    READ MODEL COMMAND HANDLER DTO BUILDER DB event
  25. 47.
  26. 48.

    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 ; } }
  27. 49.

    COMMAND HANDLER turns a command into events uses an aggregate

    to protect invariants publishes events
  28. 50.

    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 ) ; } / / . . . }
  29. 51.

    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 ( ) ) ) ; } / / . . . }
  30. 53.

    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 ) ; } / / . . . }
  31. 56.

    READ MODEL READ MODEL WRITE MODEL DB CLIENT command orm

    persists dto READ MODEL COMMAND HANDLER DTO BUILDER RDB event KVS DDB
  32. 58.

    BTW EVENTSTORMING workshop format to detect commands & events of

    the business with domain experts introduced by Alberto Brandolini
  33. 60.

    READ MODEL READ MODEL WRITE MODEL DB CLIENT command orm

    persists dto READ MODEL COMMAND HANDLER DTO BUILDER RDB event KVS DDB
  34. 61.

    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
  35. 62.

    READ MODEL READ MODEL WRITE MODEL ES CLIENT command event

    dto READ MODEL COMMAND HANDLER DTO BUILDER RDB event KVS DDB
  36. 63.

    CHANGING STATE an aggregate records its own new events newly

    recorded events are appended to an event store
  37. 64.

    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 ) ; } / / . . . }
  38. 65.

    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 ( ) ) ; } / / . . . }
  39. 66.

    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 ) ; } / / . . . }
  40. 67.

    INVARIANTS an aggregate needs history to protect invariants apply all

    events to the aggregate to rebuild relevant state
  41. 68.

    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 ) { } / / . . . }
  42. 69.

    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 + + ; } }
  43. 70.

    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
  44. 71.

    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 ( ) ;
  45. 72.

    } }

  46. 73.

    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 ( / * . . * / ) ) ; } / / . . . }
  47. 74.

    IMMUTABILITY an event store is append only, no database migrations

    needed messages are immutable, event versioning is needed tests don't need adaptation
  48. 76.

    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
  49. 78.

    READ MODEL READ MODEL WRITE MODEL ES CLIENT command event

    dto READ MODEL COMMAND HANDLER DTO BUILDER RDB event KVS DDB
  50. 82.

    READ MODEL READ MODEL WRITE MODEL ES CLIENT command event

    dto READ MODEL COMMAND HANDLER DTO BUILDER RDB event KVS DDB MQ
  51. 84.

    READ MODEL READ MODEL WRITE MODEL ES CLIENT command event

    query READ MODEL COMMAND HANDLER DTO BUILDER RDB event KVS DDB MQ
  52. 85.

    EVENTUAL CONSISTENCY clients get immediate ACK-NACK projectors receive events asynchronously

    read-models are not always up to date read-models are always valid
  53. 87.

    READ MODEL READ MODEL WRITE MODEL ES CLIENT command event

    dto READ MODEL COMMAND HANDLER DTO BUILDER RDB event KVS DDB MQ
  54. 93.
  55. 94.

    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
  56. 97.
  57. 100.

    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