Slide 1

Slide 1 text

1/101 Event Sourcing Let's write some history https://joind.in/talk/3375d

Slide 2

Slide 2 text

Willem-Jan Zijderveld @willemjanz github.com/wjzijderveld  

Slide 3

Slide 3 text

Rotterdam http://labs.qandidate.com

Slide 4

Slide 4 text

Broadway Event Sourcing/CQRS library for PHP github.com/qandidate-labs/broadway

Slide 5

Slide 5 text

Event Sourcing

Slide 6

Slide 6 text

You are throwing away data!

Slide 7

Slide 7 text

Only state gets stored

Slide 8

Slide 8 text

Only the most recent state gets stored

Slide 9

Slide 9 text

What was the previous state?

Slide 10

Slide 10 text

Why did the state change?

Slide 11

Slide 11 text

It's hard to keep track of all the data

Slide 12

Slide 12 text

An example

Slide 13

Slide 13 text

A company has one or more connected accounts

Slide 14

Slide 14 text

A company has one or more enabled applications

Slide 15

Slide 15 text

An account has access to one or more applications

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

Complicated solutions to store and retrieve the data

Slide 18

Slide 18 text

But we manage Until...

Slide 19

Slide 19 text

Boss: Which accounts received access to application X in December?

Slide 20

Slide 20 text

"We'll add a timestamp and we will know" From that point in time

Slide 21

Slide 21 text

With CRUD you lose information You only store the last known information

Slide 22

Slide 22 text

Another example

Slide 23

Slide 23 text

Revoking access to an application for an account Just remove the row in our join table

Slide 24

Slide 24 text

You lose when the access was revoked

Slide 25

Slide 25 text

You lose why it got revoked

Slide 26

Slide 26 text

You lose for which application it was revoked

Slide 27

Slide 27 text

You wouldn't know anything anymore

Slide 28

Slide 28 text

Let's assume you have a soft-delete in place

Slide 29

Slide 29 text

Boss: I want to know for which applications access got revoked more than once in the last year

Slide 30

Slide 30 text

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 Are you going to do this for all actions on accounts?

Slide 31

Slide 31 text

How does Event Sourcing help me with that?

Slide 32

Slide 32 text

Store your data in a di erent way

Slide 33

Slide 33 text

Record what has happened The resulting state becomes a natural effect

Slide 34

Slide 34 text

EventStream

Slide 35

Slide 35 text

A serie of facts CompanyRegistered AppEnabled AccountConnected AccessGrantedToApp

Slide 36

Slide 36 text

Been there, done that

Slide 37

Slide 37 text

Single source of truth One source to rule all state The events cannot lie, it happened, deal with it

Slide 38

Slide 38 text

You have been working with this system

Slide 39

Slide 39 text

Your version control is event sourced

Slide 40

Slide 40 text

Sure thing, but how? "It depends"

Slide 41

Slide 41 text

Event Sourcing DDD CQRS All 3 are optional, but work very nice together

Slide 42

Slide 42 text

Domain Driven Design Let the events tell the story The business should be re ected in your code

Slide 43

Slide 43 text

Entity An model with an identity

Slide 44

Slide 44 text

Aggregate root Responsible for keeping a group of entities consistent

Slide 45

Slide 45 text

I want to create/update my model How do I do that?

Slide 46

Slide 46 text

Record what happened

Slide 47

Slide 47 text

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 ; }

Slide 48

Slide 48 text

But we want to record an event

Slide 49

Slide 49 text

< ? 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 ; }

Slide 50

Slide 50 text

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 ) ; }

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

Save your events in an eventstore

Slide 53

Slide 53 text

Current situation

Slide 54

Slide 54 text

Reloading your model

Slide 55

Slide 55 text

< 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 ; }

Slide 56

Slide 56 text

< ? 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 ) ; } }

Slide 57

Slide 57 text

Domain Message A message to tell your application what happened

Slide 58

Slide 58 text

DomainMessage Identi er Sequencenumber Payload Timestamp Metadata

Slide 59

Slide 59 text

Identi er + sequence number

Slide 60

Slide 60 text

Payload The event itself, it tells you what happened

Slide 61

Slide 61 text

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 }

Slide 62

Slide 62 text

Timestamp It tells you when it happened

Slide 63

Slide 63 text

Metadata Descriptive, not structural

Slide 64

Slide 64 text

Let's look at some other events CompanyRegistered AppEnabled AccountConnected AccessGrantedToApp

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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 ] = $ i d ; }

Slide 67

Slide 67 text

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 ; }

Slide 68

Slide 68 text

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 ] = $ a c c o u n t I d ; }

Slide 69

Slide 69 text

We can retrieve all AccessGranted events for December

Slide 70

Slide 70 text

Won't this be slow?

Slide 71

Slide 71 text

CQRS Command Query Responsibility Segregation

Slide 72

Slide 72 text

Separate your writes and your reads

Slide 73

Slide 73 text

Read Models Create them from events using projections Speci c read models for speci c views in your application

Slide 74

Slide 74 text

EventBus All events go onto the event bus after being saved

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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 ) ; } }

Slide 77

Slide 77 text

Use the right tool for the right job

Slide 78

Slide 78 text

Possibilities are endless

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

So.. how does this ow work in an application?

Slide 81

Slide 81 text

No content

Slide 82

Slide 82 text

The C in CQRS

Slide 83

Slide 83 text

The write part of CQRS

Slide 84

Slide 84 text

Command & CommandHandler A CommandHandler deals with Commands and communicates with the Aggregate root

Slide 85

Slide 85 text

From Controller to CommandBus 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 ' ) ) ) ; } }

Slide 86

Slide 86 text

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 ) ; } }

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

From 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 ) ; }

Slide 89

Slide 89 text

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 ) ; } }

Slide 90

Slide 90 text

Quite a bit to chew

Slide 91

Slide 91 text

No content

Slide 92

Slide 92 text

But we are not done yet!

Slide 93

Slide 93 text

Testing

Slide 94

Slide 94 text

Scenario based testing Given - When - Then

Slide 95

Slide 95 text

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 ) ] ) ;

Slide 96

Slide 96 text

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 ) ] ) ;

Slide 97

Slide 97 text

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 ' ) ] ) ;

Slide 98

Slide 98 text

Time travel is possible! Use events you recorded to create a new report multiple years after the fact

Slide 99

Slide 99 text

You made a mistake in a projection? So what? Correct your projector and recreate your read model from your event stream

Slide 100

Slide 100 text

Questions? Feedback: https://joind.in/talk/3375d @willemjanz Freenode: #qandidate

Slide 101

Slide 101 text

More information http://codebetter.com/gregyoung/2010/02/20/why-use- event-sourcing/ http://codebetter.com/gregyoung/2010/02/13/cqrs-and- event-sourcing/ http://martinfowler.com/eaaDev/EventSourcing.html http://martinfowler.com/bliki/CQRS.html http://www.axonframework.org/docs/2.3/domain- modeling.html http://labs.qandidate.com/