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

CQRS + Event sourcing

CQRS + Event sourcing

Achieving a 100% auditable application log with CQRS and Event sourcing

Arnab Datta

May 23, 2019
Tweet

More Decks by Arnab Datta

Other Decks in Programming

Transcript

  1. Challenges • We need to look at the current state

    to calculate fines • We also need to look at the whole history of the state to fully understand the fines
  2. Challenges • We need to look at the current state

    to calculate fines • We also need to look at the whole history of the state to fully understand the fines • Customer also wants business analytics based on history
  3. Goals for changes in the system • Who made the

    change • When did the change happen
  4. Goals for changes in the system • Who made the

    change • When did the change happen • What was the previous change
  5. Goals for changes in the system • Who made the

    change • When did the change happen • What was the previous change • Why did the change happen
  6. Examples of commands data class KlagesakCreateCommand( override val principal: Principal,

    override val gebyrId: GebyrId, val principalRole: ActionRole, val klagesakId: KlagesakId, val reference: Long, val body: String, val attachments: List<AttachmentReference>, val email: String?, override val commandId: UUID = UUID.randomUUID() ) : • An intent to do something • Not guaranteed to succeed
  7. ES

  8. Examples of events data: { "gebyrId": “gebyr:94668137", "principalRole": "ADMIN", "klagesakId":

    "37cf7d9c-3183-41f6-8094-b44d8a855528", "reference": 60078, "email": "[email protected]", "klageAarsak": null } meta: { “time”: “2019-05-15T08:44:21.382Z”, “principalId”: “selskapUser:0200:testtffadm” }
  9. Some domain concepts • 1 Kjøretøy can have multiple Gebyr

    • 1 Gebyr can have multiple Klagesak
  10. Some domain concepts • 1 Kjøretøy can have multiple Gebyr

    • 1 Gebyr can have multiple Klagesak • 1 Klagesak can have multiple Melding
  11. Some domain concepts • 1 Kjøretøy can have multiple Gebyr

    • 1 Gebyr can have multiple Klagesak • 1 Klagesak can have multiple Melding • Melding
  12. Some domain concepts • 1 Kjøretøy can have multiple Gebyr

    • 1 Gebyr can have multiple Klagesak • 1 Klagesak can have multiple Melding • Melding Aggregate Entity Entity Entity
  13. Aggregate • Represents a transaction boundary (i.e. everything that is

    contained inside this boundary, does not care about what the rest of the world does)
  14. Aggregate • Represents a transaction boundary (i.e. everything that is

    contained inside this boundary, does not care about what the rest of the world does) • The “root” of your data
  15. Aggregate • Represents a transaction boundary (i.e. everything that is

    contained inside this boundary, does not care about what the rest of the world does) • The “root” of your data • Example: Your vehicle. All the fines related to your vehicle are contained inside this boundary and do not interact with fines on other vehicles.
  16. Entity • Similar to an aggregate, but cannot exist on

    its own. • Example: Every gebyr related to your vehicle is self- contained, but a gebyr cannot exist without a vehicle
  17. So why GraphQL? • Fits well with CQRS: GraphQL mutations

    translate to commands and GraphQL queries translate to queries
  18. So why GraphQL? • Fits well with CQRS: GraphQL mutations

    translate to commands and GraphQL queries translate to queries • Authentication
  19. So why GraphQL? • Fits well with CQRS: GraphQL mutations

    translate to commands and GraphQL queries translate to queries • Authentication • Flexibility: multiple views share same endpoint while customising the data received
  20. So why GraphQL? • Fits well with CQRS: GraphQL mutations

    translate to commands and GraphQL queries translate to queries • Authentication • Flexibility: multiple views share same endpoint while customising the data received • Direct mapping from domain objects to API responses
  21. Write vs Read model Write model (aggregates) • Unidirectional •

    Immutable • Serializable • Versioned • Highly cacheable
  22. Write vs Read model Write model (aggregates) • Unidirectional •

    Immutable • Serializable • Versioned • Highly cacheable Read model
  23. Write vs Read model Write model (aggregates) • Unidirectional •

    Immutable • Serializable • Versioned • Highly cacheable Read model • Bidirectional (forms a graph)
  24. Write vs Read model Write model (aggregates) • Unidirectional •

    Immutable • Serializable • Versioned • Highly cacheable Read model • Bidirectional (forms a graph) • Non-serializable
  25. Write vs Read model Write model (aggregates) • Unidirectional •

    Immutable • Serializable • Versioned • Highly cacheable Read model • Bidirectional (forms a graph) • Non-serializable • Context specific
  26. Write vs Read model Write model (aggregates) • Unidirectional •

    Immutable • Serializable • Versioned • Highly cacheable Read model • Bidirectional (forms a graph) • Non-serializable • Context specific • Lazily evaluated
  27. The good • Incredibly easy to implement business analytics on

    existing data • Easy to formalise write rules for your data without relying on database triggers (and therefore not worrying about consistency across different database tables)
  28. The good • Incredibly easy to implement business analytics on

    existing data • Easy to formalise write rules for your data without relying on database triggers (and therefore not worrying about consistency across different database tables) • Major performance gains possible
  29. The good • Incredibly easy to implement business analytics on

    existing data • Easy to formalise write rules for your data without relying on database triggers (and therefore not worrying about consistency across different database tables) • Major performance gains possible • Not a single table join (okay, just a few here and there)
  30. The good • Incredibly easy to implement business analytics on

    existing data • Easy to formalise write rules for your data without relying on database triggers (and therefore not worrying about consistency across different database tables) • Major performance gains possible • Not a single table join (okay, just a few here and there) • Authentication can be made super simple
  31. The bad • Higher threshold to begin with • Monkey-patching

    stuff in production is not so viable (still possible)
  32. The bad • Higher threshold to begin with • Monkey-patching

    stuff in production is not so viable (still possible) • Complexity is much higher than in a regular CRUD system ⚠
  33. The bad • Higher threshold to begin with • Monkey-patching

    stuff in production is not so viable (still possible) • Complexity is much higher than in a regular CRUD system ⚠ • Slightly niche way of doing this, which makes googling harder
  34. Not covered in this talk • How we achieved consistency

    across simultaneous writes • Projections (central to search capabilities)
  35. Not covered in this talk • How we achieved consistency

    across simultaneous writes • Projections (central to search capabilities) • Details of authentication
  36. Not covered in this talk • How we achieved consistency

    across simultaneous writes • Projections (central to search capabilities) • Details of authentication • Database structure (application is database agnostic)
  37. Not covered in this talk • How we achieved consistency

    across simultaneous writes • Projections (central to search capabilities) • Details of authentication • Database structure (application is database agnostic) • Choice of language (it matters)
  38. Q/A