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

Let's write some History - PHPBenelux 2016

Let's write some History - PHPBenelux 2016

In this talk I’m going to explain how Event Sourcing can help you solve this problem and what other benefits you can get from it.
We will walk over of the concepts and possible implementations of event sourcing.

Willem-Jan Zijderveld

January 30, 2016
Tweet

More Decks by Willem-Jan Zijderveld

Other Decks in Programming

Transcript

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

    View Slide

  2. Willem-Jan Zijderveld
    @willemjanz
    github.com/wjzijderveld


    View Slide

  3. Rotterdam
    http://labs.qandidate.com

    View Slide

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

    View Slide

  5. Event Sourcing

    View Slide

  6. You are throwing away data!

    View Slide

  7. Only state gets
    stored

    View Slide

  8. Only the most
    recent state
    gets stored

    View Slide

  9. What was the previous
    state?

    View Slide

  10. Why did the state
    change?

    View Slide

  11. It's hard to keep track of
    all the data

    View Slide

  12. An example

    View Slide

  13. A company has one or
    more connected accounts

    View Slide

  14. A company has one or
    more enabled applications

    View Slide

  15. An account has access to
    one or more applications

    View Slide

  16. View Slide

  17. Complicated solutions to store and
    retrieve the data

    View Slide

  18. But we manage
    Until...

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  22. Another example

    View Slide

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

    View Slide

  24. You could lose when the access was
    revoked

    View Slide

  25. You could lose why it got revoked

    View Slide

  26. You could lose for which application
    it was revoked

    View Slide

  27. You wouldn't know anything anymore

    View Slide

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

    View Slide

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

    View Slide

  30. 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?

    View Slide

  31. How does Event Sourcing
    help me with that?

    View Slide

  32. Store your data in
    a different way

    View Slide

  33. Record what has happened
    The resulting state becomes a natural effect

    View Slide

  34. EventStream

    View Slide

  35. A serie of facts
    CompanyRegistered
    AppEnabled
    AccountConnected
    AccessGrantedToApp

    View Slide

  36. Been there, done that

    View Slide

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

    View Slide

  38. You have been working with this
    system

    View Slide

  39. Your version control is
    event sourced

    View Slide

  40. Sure thing, but how?
    "It depends"

    View Slide

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

    View Slide

  42. Domain Driven Design
    Let the events tell the story
    The business should be reflected in your code

    View Slide

  43. Entity
    An model with an identity

    View Slide

  44. Aggregate root
    Responsible for keeping a group of entities consistent

    View Slide

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

    View Slide

  46. Record what happened

    View Slide

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

    View Slide

  48. But we want to record an
    event

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  52. Save your events in an
    eventstore

    View Slide

  53. Current situation

    View Slide

  54. Reloading your model

    View Slide

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

    View Slide

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

    View Slide

  57. Domain Message
    A message to tell your application what happened

    View Slide

  58. DomainMessage
    Identifier
    Sequencenumber
    Payload
    Timestamp
    Metadata

    View Slide

  59. Identifier + sequence
    number

    View Slide

  60. Payload
    The event itself, it tells you what
    happened

    View Slide

  61. 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
    }

    View Slide

  62. Timestamp
    It tells you when it happened

    View Slide

  63. Metadata
    Descriptive, not structural

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  69. We can retrieve all
    AccessGranted events for
    December

    View Slide

  70. Won't this be slow?

    View Slide

  71. CQRS
    Command Query Responsibility Segregation

    View Slide

  72. Separate your writes and
    your reads

    View Slide

  73. Read Models
    Create them from events using projections
    Specific read models for specific views in your application

    View Slide

  74. EventBus
    All events go onto the event bus after being saved

    View Slide

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

    View Slide

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

    View Slide

  77. Use the right tool for the
    right job

    View Slide

  78. Possibilities are endless

    View Slide

  79. 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

    View Slide

  80. So.. how does this flow
    work in an application?

    View Slide

  81. View Slide

  82. The C in CQRS

    View Slide

  83. The write part of CQRS

    View Slide

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

    View Slide

  85. 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
    '
    )
    )
    )
    ;
    }
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  90. Quite a bit to chew

    View Slide

  91. View Slide

  92. But we are not done yet!

    View Slide

  93. Testing

    View Slide

  94. Scenario based testing
    Given - When - Then

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  100. Questions?
    https://joind.in/talk/6bf57
    @willemjanz
    Freenode: #qandidate

    View Slide

  101. 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/

    View Slide