$30 off During Our Annual Pro Sale. View Details »

Let's write some history - DPC 2015

Let's write some history - DPC 2015

45 minute talk on Event Sourcing. Given on Dutch PHP Conference 2015

Willem-Jan Zijderveld

June 26, 2015
Tweet

More Decks by Willem-Jan Zijderveld

Other Decks in Programming

Transcript

  1. Boss: I want to know for which applications access got

    revoked more than once in the last year
  2. Keep track of revocations account_id app_id datetime 75623 8 2015-01-01T00:00:00+0000

    75623 8 2015-01-27T18:54:18+0000 Past revocations are gone
  3. Single source of truth One source to rule all state

    The events cannot lie, it happened, deal with it
  4. You normally might do it like this < ? p

    h p / / C o m p a n y . p h p p u b l i c f u n c t i o n _ _ c o n s t r u c t ( $ i d , $ n a m e ) { $ t h i s - > i d = $ i d ; $ t h i s - > n a m e = $ n a m e ; }
  5. < ? p h p / / C o m

    p a n y . p h p p u b l i c s t a t i c f u n c t i o n r e g i s t e r ( $ i d , $ n a m e ) { $ c o m p a n y = n e w C o m p a n y ( ) ; $ c o m p a n y - > a p p l y ( n e w C o m p a n y R e g i s t e r e d E v e n t ( $ i d , $ n a m e ) ) ; r e t u r n $ c o m p a n y ; }
  6. Record the events < p h p / / A

    g g r e g a t e R o o t . p h p p u b l i c f u n c t i o n a p p l y ( $ e v e n t ) { $ t h i s - > h a n d l e ( $ e v e n t ) ; $ t h i s - > u n c o m m i t e d E v e n t s [ ] = $ e v e n t ; } p r i v a t e f u n c t i o n h a n d l e ( $ e v e n t ) { $ c l a s s P a r t s = e x p l o d e ( ' \ \ ' , $ e v e n t ) ; $ m e t h o d = ' a p p l y ' . e n d ( $ c l a s s P a r t s ) ; $ t h i s - > $ m e t h o d ( $ e v e n t ) ; } Just one possible implementation
  7. / / C o m p a n y .

    p h p p u b l i c f u n c t i o n a p p l y C o m p a n y R e g i s t e r e d E v e n t ( C o m p a n y R e g i s t e r e d E v e n t $ e v e n t ) { $ t h i s - > c o m p a n y I d = $ e v e n t - > g e t C o m p a n y I d ( ) ; $ t h i s - > c o m p a n y N a m e = $ e v e n t - > g e t C o m p a n y N a m e ( ) ; }
  8. < p h p / / C o m p

    a n y R e p o s i t o r y . p h p f u n c t i o n l o a d ( $ a g g r e g a t e I d ) { $ e v e n t s = $ t h i s - > e v e n t S t o r e - > l o a d ( $ a g g r e g a t e I d ) ; $ a g g r e g a t e = n e w $ t h i s - > a g g r e g a t e C l a s s ( ) ; $ a g g r e g a t e - > i n i t i a l i z e S t a t e ( $ e v e n t s ) ; r e t u r n $ a g g r e g a t e ; }
  9. < ? p h p / / C o m

    p a n y . p h p p u b l i c f u n c t i o n i n i t i a l i z e S t a t e ( a r r a y $ e v e n t s ) { f o r e a c h ( $ e v e n t s a s $ e v e n t ) { $ t h i s - > h a n d l e ( $ e v e n t ) ; } }
  10. Should contain everything It should only depend on previous events

    f i n a l c l a s s C o m p a n y R e g i s t e r e d E v e n t { p r i v a t e $ c o m p a n y I d ; p r i v a t e $ c o m p a n y N a m e ; / / c o n s t r u c t o r + g e t t e r s }
  11. CompanyRegistered / / C o m p a n y

    f u n c t i o n a p p l y C o m p a n y R e g i s t e r e d E v e n t ( C o m p a n y R e g i s t e r e d E v e n t $ e v e n t ) { $ t h i s - > c o m p a n y I d = $ e v e n t - > g e t C o m p a n y I d ( ) ; }
  12. AccountConnected / / C o m p a n y

    f u n c t i o n a p p l y A c c o u n t C o n n e c t e d E v e n t ( A c c o u n t C o n n e c t e d E v e n t $ e v e n t ) { $ i d = $ e v e n t - > g e t A c c o u n t I d ( ) ; $ t h i s - > a c c o u n t s [ $ i d ] = $ e v e n t - > g e t A c c o u n t I d ( ) ; }
  13. AppEnabled / / C o m p a n y

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

    p r o t e c t e d f u n c t i o n g e t C h i l d E n t i t i e s ( ) { r e t u r n $ t h i s - > s u b s c r i p t i o n s ; } / / S u b s c r i p t i o n f u n c t i o n a p p l y A c c e s s G r a n t e d T o A p p E v e n t ( A c c e s s G r a n t e d T o A p p E v e n t $ e v e n t ) { i f ( $ t h i s - > a p p I d ! = = $ e v e n t - > g e t A p p I d ( ) ) { r e t u r n ; } $ a c c o u n t I d = $ e v e n t - > g e t A c c o u n t I d ( ) ; $ t h i s - > g r a n t e d A c c o u n t s [ $ a c c o u n t I d ] = $ e v e n t - > g e t A c c o u n t I d ( ) ; }
  15. Read Models Create them from events using projections Specific read

    models for specific views in your application
  16. CompanyRegistration c l a s s C o m p

    a n y R e g i s t r a t i o n i m p l e m e n t s R e a d M o d e l { p u b l i c f u n c t i o n _ _ c o n s t r u c t ( $ c o m p a n y I d , $ c o m p a n y N a m e , D a t e T i m e $ r e g i s t e r e d O n ) { / / . . } }
  17. Company Registration Projector c l a s s C o

    m p a n y R e g i s t r a t i o n P r o j e c t o r i m p l e m e n t s E v e n t L i s t e n e r { p u b l i c f u n c t i o n a p p l y C o m p a n y R e g i s t e r e d E v e n t ( C o m p a n y R e g i s t e r e d E v e n t $ e v e n t , D o m a i n M e s s a g e $ d o m a i n M e s s a g e ) { $ c o m p a n y = n e w C o m p a n y R e g i s t r a t i o n ( $ e v e n t - > g e t C o m p a n y I d ( ) , $ e v e n t - > g e t C o m p a n y N a m e ( ) , $ d o m a i n M e s s a g e - > g e t R e c o r d e d O n ( ) ) ; $ t h i s - > r e p o s i t o r y - > s a v e ( $ c o m p a n y ) ; } }
  18. The ability to create multiple read models List of company

    registrations Graph of all connections between companies and accounts Creating reports about the amount of revocations
  19. From Controller to CommandHandler c l a s s C

    o m p a n y C o n t r o l l e r { f u n c t i o n r e g i s t e r A c t i o n ( R e q u e s t $ r e q u e s t ) { $ t h i s - > c o m m a n d B u s - > d i s p a t c h ( n e w R e g i s t e r C o m p a n y C o m m a n d ( U u i d : : u u i d 4 ( ) , $ r e q u e s t - > r e q u e s t - > g e t ( ' c o m p a n y N a m e ' ) ) ) ; } }
  20. From CommandHandler to Aggregate c l a s s C

    o m p a n y C o m m a n d H a n d l e r { f u n c t i o n h a n d l e R e g i s t e r C o m p a n y C o m m a n d ( R e g i s t e r C o m p a n y C o m m a n d $ c o m m a n d ) { $ c o m p a n y = C o m p a n y : : r e g i s t e r ( $ c o m m a n d - > g e t C o m p a n y I d ( ) , $ c o m m a n d - > g e t C o m p a n y N a m e ( ) ) ; $ t h i s - > a g g r e g a t e R e p o s t i t o r y - > s a v e ( $ c o m p a n y ) ; } }
  21. From Aggregate to Event p u b l i c

    s t a t i c f u n c t i o n r e g i s t e r ( $ c o m p a n y I d , $ n a m e ) { $ c o m p a n y = n e w C o m p a n y ( ) ; $ c o m p a n y - > a p p l y ( n e w C o m p a n y R e g i s t e r e d E v e n t ( $ c o m p a n y I d , $ n a m e ) ) ; r e t u r n $ c o m p a n y ; } p u b l i c f u n c t i o n a p p l y C o m p a n y R e g i s t e r e d E v e n t ( C o m p a n y R e g i s t e r e d E v e n t $ e v e n t ) { $ t h i s - > c o m p a n y I d = $ e v e n t - > g e t C o m p a n y I d ( ) ; }
  22. From Aggregate Repository to EventStore / / C o m

    p a n y R e g i s t r y . p h p f u n c t i o n s a v e ( $ a g g r e g a t e ) { $ e v e n t s = $ a g g r e g a t e - > g e t U n c o m m i t t e d E v e n t s ( ) ; $ t h i s - > e v e n t S t o r e - > a p p e n d ( $ a g g r e g a t e - > g e t A g g r e g a t e I d ( ) , $ e v e n t s ) ; $ t h i s - > e v e n t B u s - > p u b l i s h ( $ e v e n t s ) ; }
  23. From event to read model c l a s s

    C o m p a n y R e g i s t r a t i o n P r o j e c t o r { p u b l i c f u n c t i o n a p p l y C o m p a n y R e g i s t e r e d E v e n t ( C o m p a n y R e g i s t e r e d E v e n t $ e v e n t , D o m a i n M e s s a g e $ d o m a i n M e s s a g e ) { $ c o m p a n y = n e w C o m p a n y R e g i s t r a t i o n ( $ e v e n t - > g e t C o m p a n y I d ( ) , $ e v e n t - > g e t C o m p a n y N a m e ( ) , $ d o m a i n M e s s a g e - > g e t R e c o r d e d O n ( ) ) ; $ t h i s - > r e p o s i t o r y - > s a v e ( $ c o m p a n y ) ; } }
  24. Controller -> CommandBus -> CommandHandler -> AggregateRoot -> Event ->

    EventStore -> EventBus -> Projectors -> Read Model
  25. Test your Command Handler $ t h i s -

    > s c e n a r i o - > g i v e n ( [ n e w C o m p a n y R e g i s t e r e d E v e n t ( 1 2 3 ) ] ) - > w h e n ( n e w E n a b l e A p p F o r C o m p a n y C o m m a n d ( 4 2 , 1 2 3 ) ) - > t h e n ( [ n e w A p p E n a b l e d E v e n t ( 4 2 , 1 2 3 ) ] ) ;
  26. Or your aggregate $ t h i s - >

    s c e n a r i o - > g i v e n ( [ n e w C o m p a n y R e g i s t e r e d E v e n t ( 1 2 3 ) ] ) - > w h e n ( f u n c t i o n ( $ c o m p a n y ) { $ c o m p a n y - > e n a b l e A p p ( 4 2 ) ; } ) - > t h e n ( [ n e w A p p E n a b l e d E v e n t ( 4 2 , 1 2 3 ) ] ) ;
  27. Don't forget your read models $ t h i s

    - > s c e n a r i o - > g i v e n ( [ ] ) - > w h e n ( n e w C o m p a n y R e g i s t e r e d E v e n t ( 1 2 3 , ' A c m e I n c ' ) ) - > t h e n ( [ n e w C o m p a n y R e g i s t r a t i o n ( 1 2 3 , ' A c m e I n c ' ) ] ) ;
  28. Time travel is possible! Use events you recorded to create

    a new report multiple years after the fact
  29. You made a mistake in a projection? So what? Correct

    your projector and recreate your read model from your event stream