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

A Functional Foundation for Event Sourced Apps

A Functional Foundation for Event Sourced Apps

It’s often said in the ES/CQRS community that frameworks are not needed because the basic operations are quite trivial. This is true, as we are going to see during this talk. However, it’s not that trivial to deal with failure, concurrency, and IO. Even less when we are building reactive systems.

It turns out that functional programming offers many constructs that can help us to deal with all those aspects while staying pure and principled.
We are convinced that a solid functional foundation can lay the path for better abstractions and more expressive modeling.

43d6fcbec33838cabaa3040f0753c893?s=128

Renato Cavalcanti

October 19, 2017
Tweet

Transcript

  1. A Functional Foundation for Event Sourced Applications Photo by Tom

    Van Hoogstraten on Unsplash 1/24
  2. Who am I? Renato Cavalcanti * Member of Lagom Team

    at Lightbend * Founder of BeScala, the Belgian Scala User Group * Author of Fun.CQRS renato@lightbend.com @renatocaval Reactive Summit, Austin, 2017 2/24
  3. Before we start... What we are going to see is

    not an implementation. We are going to analyse some functions and types at a higher level. Reactive Summit, Austin, 2017 3/24
  4. Event Sourcing, CQRS and DDD Reactive Summit, Austin, 2017 4/24

  5. Event Sourcing, CQRS and DDD — Event Sourcing happens when

    you store every single domain event. Reactive Summit, Austin, 2017 4/24
  6. Event Sourcing, CQRS and DDD — Event Sourcing happens when

    you store every single domain event. — CQRS is only about having two models, write and read models. Reactive Summit, Austin, 2017 4/24
  7. Event Sourcing, CQRS and DDD — Event Sourcing happens when

    you store every single domain event. — CQRS is only about having two models, write and read models. — Aggregate is a term borrowed from DDD and very often used as synonym to write-model Reactive Summit, Austin, 2017 4/24
  8. The Functions // Command handler validates and emits events Aggregate

    => Command => Seq[Event] Reactive Summit, Austin, 2017 5/24
  9. The Functions // Command handler validates and emits events Aggregate

    => Command => Seq[Event] // Event handler applies an event on the Aggregate Aggregate => Event => Aggregate Reactive Summit, Austin, 2017 6/24
  10. The Functions // Command handler validates and emits events Aggregate

    => Command => Seq[Event] // Event handler applies an event on the Aggregate Aggregate => Event => Aggregate // why not this one instead? Aggregate => Command => (Aggregate, Seq[Event]) Reactive Summit, Austin, 2017 7/24
  11. A Command emits zero, one or more events, but it

    can also be rejected // Command handler wraps Events in F[G[_]] Aggregate => Command => F[G[Event]] // Event handler Aggregate => Event => Aggregate Reactive Summit, Austin, 2017 8/24
  12. Understanding F[G[Event]] — F is a Context that can express

    success or failure — G is a Context that can express quantity (zero, one or more) For example... Aggregate => Command => Try[Seq[Event]] // F = Try, G = Seq Reactive Summit, Austin, 2017 9/24
  13. Understanding F[G[Event]] — F is a Context that can express

    success or failure — G is a Context that can express quantity (zero, one or more) But if I want to emit one single Event and express the fact that it always succeeds? Reactive Summit, Austin, 2017 10/24
  14. Understanding F[G[Event]] — F is a Context that can express

    success or failure — G is a Context that can express quantity (zero, one or more) type Id[T] = T Aggregate => Command => Id[Id[Event]] // F = Id, G = Id Aggregate => Command => Event Aggregate => Command => Id[Seq[Event]] // F = Id, G = Seq Aggregate => Command => Seq[Event] Reactive Summit, Austin, 2017 11/24
  15. Understanding F[G[Event]] — F is a Context that can express

    success or failure — G is a Context that can express quantity (zero, one or more) Aggregate => Command => Try[Id[Event]] // F = Try, G = Id Aggregate => Command => Try[Event] Aggregate => Command => Try[Option[Event]] // F = Try, G = Option Aggregate => Command => Try[Seq[Event]] // F = Try, G = Seq Reactive Summit, Austin, 2017 12/24
  16. Understanding F[G[Event]] — F is a Context that can express

    success or failure — G is a Context that can express quantity (zero, one or more) type Error = String type Result[T] = Either[Error, T] Aggregate => Command => Result[Id[Event]] // F = Result, G = Id Aggregate => Command => Result[Option[Event]] // F = Result, G = Option Aggregate => Command => Result[Seq[Event]] // F = Result, G = Seq Reactive Summit, Austin, 2017 13/24
  17. Different Contexts Different Commands may require different Fs and Gs

    type Id[T] = T case class Account(balance: Double) Account => Deposit => Id[Id[Deposited]] // F = Id, G = Id Account => Deposit => Deposited Account => Withdraw => Try[Id[Withdrawn]] // F = Try, G = Id Account => Withdraw => Try[Withdrawn] Reactive Summit, Austin, 2017 14/24
  18. In the beginning, there is None type State = Option[Aggregate]

    // Command handler State => Command => F[G[Event]] // Event handler State => Event => State Reactive Summit, Austin, 2017 15/24
  19. An Aggregate aggregates its past type History = Seq[Event] //

    all events since the seed-event type State = Option[Aggregate] Reactive Summit, Austin, 2017 16/24
  20. An Aggregate aggregates its past type History = Seq[Event] //

    all events since the seed-event type State = Option[Aggregate] — when History == Seq.empty then Option[Aggregate] == None Reactive Summit, Austin, 2017 16/24
  21. An Aggregate aggregates its past type History = Seq[Event] //

    all events since the seed-event type State = Option[Aggregate] — when History == Seq.empty then Option[Aggregate] == None — when History != Seq.empty then Option[Aggregate] == Some[Aggregate] Reactive Summit, Austin, 2017 16/24
  22. An Aggregate aggregates its past type History = Seq[Event] //

    all events since the seed-event type State = Option[Aggregate] — when History == Seq.empty then Option[Aggregate] == None — when History != Seq.empty then Option[Aggregate] == Some[Aggregate] — given any History and a Event Handler we can foldLeft over the past events and rebuild the Aggregate. Reactive Summit, Austin, 2017 16/24
  23. An Aggregate aggregates its past type History = Seq[Event] type

    State = Option[Aggregate] val eventHandler: State => Event => State = ... val pastEvents: History = ... pastEvents.foldLeft(None:State) { (agg, evt) => eventHandler(agg)(evt) } Reactive Summit, Austin, 2017 17/24
  24. Being Opinionated Those are the foundation functions, but you may

    implement them in many different forms according to your own style, taste and/or constraints. Reactive Summit, Austin, 2017 18/24
  25. Being Opinionated Option[Aggregate] => Command => F[G[Event]] Option[Aggregate] => Event

    => Option[Aggregate] // We can split the functions () => Command => F[G[Event]] Aggregate => Command => F[G[Event]] () => Event => Aggregate Aggregate => Event => Aggregate Reactive Summit, Austin, 2017 19/24
  26. Being Opinionated Option[Aggregate] => Command => F[G[Event]] Option[Aggregate] => Event

    => Option[Aggregate] // Or decide to always have a default initial state val initialState: Aggregate = ... Aggregate => Command => F[G[Event]] Aggregate => Event => Aggregate Reactive Summit, Austin, 2017 20/24
  27. Being Opinionated Option[Aggregate] => Command => F[G[Event]] Option[Aggregate] => Event

    => Option[Aggregate] // initial state + exception throwing Aggregate => Command => Event Aggregate => Command => Seq[Event] Aggregate => Event => Aggregate Reactive Summit, Austin, 2017 21/24
  28. Event Sourcing is about persistence, no? Those functions are not

    expressing in any way how events are persisted. Or how they are replayed. That's made on purpose. Reactive Summit, Austin, 2017 22/24
  29. Event Sourcing is about persistence, no? Those functions are not

    expressing in any way how events are persisted. Or how they are replayed. That's made on purpose. — we must read events from journal to reconstruct State Reactive Summit, Austin, 2017 22/24
  30. Event Sourcing is about persistence, no? Those functions are not

    expressing in any way how events are persisted. Or how they are replayed. That's made on purpose. — we must read events from journal to reconstruct State — we must persist events before continue Reactive Summit, Austin, 2017 22/24
  31. Event Sourcing is about persistence, no? Those functions are not

    expressing in any way how events are persisted. Or how they are replayed. That's made on purpose. — we must read events from journal to reconstruct State — we must persist events before continue — we may want to save snapshots to avoid replaying from scratch each time Reactive Summit, Austin, 2017 22/24
  32. Event Sourcing is about persistence, no? Those functions are not

    expressing in any way how events are persisted. Or how they are replayed. That's made on purpose. — we must read events from journal to reconstruct State — we must persist events before continue — we may want to save snapshots to avoid replaying from scratch each time — we guarantee consistency in case of concurrent access Reactive Summit, Austin, 2017 22/24
  33. Event Sourcing is about persistence, no? Those functions are not

    expressing in any way how events are persisted. Or how they are replayed. That's made on purpose. — we must read events from journal to reconstruct State — we must persist events before continue — we may want to save snapshots to avoid replaying from scratch each time — we guarantee consistency in case of concurrent access — we may want to distribute the aggregates over different nodes and keep the same consistency guarantees Reactive Summit, Austin, 2017 22/24
  34. Takeaways Reactive Summit, Austin, 2017 23/24

  35. Takeaways — We need informed opinions. We must understand the

    foundation functions so we can choose what to expose and how to expose. Reactive Summit, Austin, 2017 23/24
  36. Takeaways — We need informed opinions. We must understand the

    foundation functions so we can choose what to expose and how to expose. — The closest we are to the foundation, the more options we have. Reactive Summit, Austin, 2017 23/24
  37. Takeaways — We need informed opinions. We must understand the

    foundation functions so we can choose what to expose and how to expose. — The closest we are to the foundation, the more options we have. — Not offering options is not bad in itself. It's a design decision. Reactive Summit, Austin, 2017 23/24
  38. Takeaways — We need informed opinions. We must understand the

    foundation functions so we can choose what to expose and how to expose. — The closest we are to the foundation, the more options we have. — Not offering options is not bad in itself. It's a design decision. — We should free ourselves from everything that is not domain related. This should be provided by libraries/frameworks. Reactive Summit, Austin, 2017 23/24
  39. Thank You renato@lightbend.com @renatocaval Photo by Janko Ferlič on Unsplash

    24/24