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

Designing Towards Event Sourcing

Designing Towards Event Sourcing

µCon 2019 (London, UK)

Event sourcing offers you many benefits from both business and pure programming angles. As it's such a fundamental design principle, one might argue that it should be introduced early in your designs. However, this doesn't need to be the case. Especially, when you are anticipating future requirements to be based on this but you don't have a budget to solve them yet as you are dealing with today's problems.

More: https://skillsmatter.com/legacy_profile/tugberk-ugurlu

Join Tugberk in this session where he will walk you though an evolvable software architecture based on Domain-driven Design and CQRS, which can let you optimise for today's problems at the same time allowing you to design towards event sourcing. You will also start understanding the concepts of Hexagonal Architecture pattern, which puts the Domain at the heart of the system and pushes the input and output at the edges.

This talk will give you some techniques to let you be tactical about your architectural approach, which will be especially handy when conflicts arises between business needs and refactoring costs.

Tugberk Ugurlu

May 30, 2019
Tweet

More Decks by Tugberk Ugurlu

Other Decks in Programming

Transcript

  1. 1 BasketCreated ItemAdded ItemAdded ItemRemoved ItemQuantityIncreased ItemAdded BasketCompleted 2 BasketCreated

    ItemAdded ItemQuantityIncreased ItemAdded ItemAdded 3 BasketCreated ItemAdded ItemAdded BasketCompleted Event Streams
  2. Append Only Writes BasketCreated { "id": "1", "time": 1559172146 }

    ItemAdded { "itemId": "10", "quantity": 1, "time": 1559172216 } ItemAdded { "itemId": "20", "quantity": 2, "time": 1559172225 } ItemRemoved { "itemId": "10", "time": 1559172286 } ItemQuantityIncreased { "itemId": "20", "increasedBy": 2, "time": 1559172325 } State { "id": 1, "items": [ { "id": "20", "quantity": 4 } ], "createdOn": 1559172146, "lastUpdatedOn": 1559172325, "version": 5 }
  3. Changing Projections BasketCreated { "id": "1", "time": 1559172146 } ItemAdded

    { "itemId": "10", "quantity": 1, "time": 1559172216 } ItemAdded { "itemId": "20", "quantity": 2, "time": 1559172225 } ItemRemoved { "itemId": "10", "time": 1559172286 } ItemQuantityIncreased { "itemId": "20", "increasedBy": 2, "time": 1559172325 } BasketCreated { "id": "1", "time": 1559172146 } ItemAdded { "itemId": "10", "quantity": 1, "time": 1559172216 } ItemRemoved { "itemId": "10", "time": 1559172286 } BasketCompleted { "itemId": "20", "increasedBy": 2, "time": 1559172325 } State { "completedBasketWithRemovedItems": "50%" } State { "averageItemRemoveDurationInSeconds": 70 } State { "removedItems": [ { "id": "10", "count": 2 } ] }
  4. Consistency Checks BasketCreated { "id": "1", "time": 1559172146 } ItemAdded

    (v1) { "itemId": "10", "quantity": 1, "time": 1559172216 } ItemAdded (v2) { "itemId": "20", "quantity": 2, "time": 1559172225 } ItemRemoved (v3) { "itemId": "10", "time": 1559172286 } ItemQuantityIncreased (v3) { "itemId": "10", "increasedBy": 2, "time": 1559172325 } State { "id": 1, "items": [ { "id": "20", "quantity": 2 } ], "createdOn": 1559172146, "lastUpdatedOn": 1559172286, "version": 4 }
  5. 1 BasketCreated ItemAdded ItemAdded ItemRemoved ItemQuantityIncreased ItemAdded BasketCompleted 2 BasketCreated

    ItemAdded ItemQuantityIncreased ItemAdded ItemAdded 3 BasketCreated ItemAdded ItemAdded BasketCompleted Naturally Partitioned
  6. Eventual Consistency • Read side needs to embrace eventual consistency

    • Unless you can reproject over the entire stream every time a read is requested • Which is especially very inefficient where your projection needs to work across streams
  7. Consumer Driven Subscription • Consumer dictates where the delivery needs

    to start from • Unlike AMQP model, event messages can be requested over and over again
  8. Event Order Matters • So that the state can be

    applied expectedly • Unless read models pay a special attention to the sequence of the messages and try to BasketCreated { "id": "1", "time": 1559172146 } ItemAdded { "itemId": "20", "quantity": 2, "time": 1559172225 } ItemRemoved { "itemId": "10", "time": 1559172286 } ItemAdded { "itemId": "10", "quantity": 1, "time": 1559172216 } ItemQuantityIncreased { "itemId": "20", "increasedBy": 2, "time": 1559172325 }
  9. Two Sides Low level How to coordinate the flow from

    request arriving to response going out the door, for both reads and writes. High level How the implementation fits into our end-to-end solution, where the system resources defined and being used.
  10. Aggregate • Write model • Represents the consistency boundary •

    Stream of events are tied to an aggregate. Why? ◦ Aggregate represents our consistency boundary and we can enforce the consistency within an aggregate even when applying multiple events to its state • Not a strict relationship structure ◦ Could result in very large aggregates w/o necessarily needing all the resources • Repository handles aggregate construction by retrieving and applying all the events
  11. Commands • Request to alter the state of a particular

    aggregate instance • Command is different than events ◦ commands can be rejected (e.g. due to invalidating business invariants) ◦ whereas events represent a state change which has already happened • Application of a command on an aggregate instance can cause one or multiple events to be emitted
  12. Command Handler • Sits in between command sender and the

    aggregate • Abstract away the write model from the outside world • Performs invariant checks which an aggregate simply cannot do ◦ E.g. in cases where the check needs to query an external data outside the boundaries of an aggregate instance
  13. Queries • Construct a read model which satisfy the access

    pattern • Listening on events on one aggregate or multiple, across streams • Doesn’t have to project the data on the fly, can cache and serve data faster in that way • Projection can also happen offline
  14. Hexagonal Architecture (a.k.a. Ports and Adaptors) • Architectural approach to

    encapsulate changes • Prevents infrastructure code to leak into the domain • Consists of 3 layers: ◦ Domain ◦ Application ◦ Framework • Hexagonal Architecture != Layered Architecture http://tpierrain.blogspot.com/2016/04/hexagonal-layers.html
  15. Hexagonal Architecture and Even Sourcing: Test If you change your

    data storage system and this results in making a change in the domain layer, you failed! More accurately, your abstractions failed you!
  16. More Resources • CQRS FAQ: https://cqrs.nu/Faq • Greg Young -

    CQRS and Event Sourcing - Code on the Beach 2014: https://www.youtube.com/watch?v=JHGkaShoyNs • Hexagonal Architecture: https://fideloper.com/hexagonal-architecture • Versioning in an Event Sourced System: https://leanpub.com/esversioning • CQRS Example in Go: https://github.com/jetbasrawi/go.cqrs • CQRS Example in C#: https://github.com/gregoryyoung/m-r • Distributed Sagas: A Protocol for Coordinating Microservices - Caitie McCaffrey: https://www.youtube.com/watch?v=0UTOLRTwOX0