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

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. 1/92
    Event Sourcing
    Let's write some history
    https://joind.in/14226

    View Slide

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


    View Slide

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

    View Slide

  4. Broadway
    Event Sourcing and 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 most
    recent state
    gets stored

    View Slide

  8. What was the previous
    state?

    View Slide

  9. Why did the state
    change?

    View Slide

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

    View Slide

  11. A company has one or
    more connected accounts

    View Slide

  12. A company has one or
    more enabled applications

    View Slide

  13. An account has access to
    one or more applications

    View Slide

  14. View Slide

  15. Complicated solutions to store and
    retrieve the data

    View Slide

  16. But we manage
    Until...

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  20. Revoking access to an application for
    an account

    View Slide

  21. You could lose when the access was
    revoked

    View Slide

  22. You could lose why it got revoked

    View Slide

  23. You could lose for which application
    it was revoked

    View Slide

  24. You wouldn't know anything anymore

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  28. How does Event Sourcing
    help me with that?

    View Slide

  29. Store your data in
    a different way

    View Slide

  30. Record what has changed
    The resulting state becomes a natural effect

    View Slide

  31. EventStream

    View Slide

  32. A serie of facts
    CompanyRegistered
    AppEnabled
    AccountConnected
    AccessGrantedToApp

    View Slide

  33. Been there, done that

    View Slide

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

    View Slide

  35. Your version control is
    event sourced

    View Slide

  36. Sure thing, but how?
    There isn't one true answer

    View Slide

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

    View Slide

  38. Domain Driven Design
    The business should be reflected in your code

    View Slide

  39. Entity
    An model with an identity

    View Slide

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

    View Slide

  41. I want to update my
    model
    How do I do that?

    View Slide

  42. Record the change

    View Slide

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

  44. But we want to record an
    event

    View Slide

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

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

    View Slide

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

  48. Save your events in an
    eventstore

    View Slide

  49. Reloading your model

    View Slide

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

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

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

    View Slide

  53. DomainMessage
    Identifier
    Sequencenumber
    Payload
    Timestamp
    Metadata

    View Slide

  54. Identifier + sequence
    number

    View Slide

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

    View Slide

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

  57. Timestamp
    It tells you when it happened

    View Slide

  58. Metadata
    Descriptive, not structural

    View Slide

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

    View Slide

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

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

    View Slide

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

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

    View Slide

  64. We can retrieve all
    AccessGranted events for
    December

    View Slide

  65. View Slide

  66. Won't this be slow?

    View Slide

  67. CQRS
    Command Query Responsibility Segregation

    View Slide

  68. Separate your writes and
    your reads

    View Slide

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

    View Slide

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

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

  72. Use the right tool for the
    right job

    View Slide

  73. Possibilities are endless

    View Slide

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

  75. So.. how does this flow
    work in an application?
    We'll use Commands

    View Slide

  76. The write part of CQRS

    View Slide

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

    View Slide

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

    View Slide

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

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

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

    View Slide

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

  83. Controller -> CommandBus -> CommandHandler ->
    AggregateRoot -> Event -> EventStore -> EventBus -> Projectors
    -> Read Model

    View Slide

  84. Quite a bit to chew
    Try not to reinvent everything yourself

    View Slide

  85. Testing
    Scenario based testing
    Given - When - Then

    View Slide

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

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

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

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

    View Slide

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

    View Slide

  91. Questions?
    joind.in/14226
    @willemjanz
    Freenode: #qandidate

    View Slide

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