Slide 1

Slide 1 text

Fun.CQRS Field guide to DDD/CQRS using the Scala Type System and Akka

Slide 2

Slide 2 text

Agenda • DDD / CQRS / Event Sourcing (intro) • Aggregates / Commands / Events in Scala • Type members: type projections • Partial Functions, lifting and function composition • Akka and asynchrounous programming • Akka Persistence and Event Sourcing

Slide 3

Slide 3 text

DDD - Aggregate • Aggregate is a central DDD concept.

Slide 4

Slide 4 text

DDD - Aggregate • Aggregate is a central DDD concept. • It has a root and zero or more entities and value objects underneath.

Slide 5

Slide 5 text

DDD - Aggregate • Aggregate is a central DDD concept. • It has a root and zero or more entities and value objects underneath. • Responsible for the consistency of underneath objects.

Slide 6

Slide 6 text

DDD - Aggregate • Aggregate is a central DDD concept. • It has a root and zero or more entities and value objects underneath. • Responsible for the consistency of underneath objects. • You can only modify one Aggregate per transaction.

Slide 7

Slide 7 text

CQRS - Event Sourcing • "It is simply the creation of two objects where there was previously only one" - Greg Young

Slide 8

Slide 8 text

CQRS - Event Sourcing • "It is simply the creation of two objects where there was previously only one" - Greg Young • Write Model receives Commands and produces Events.

Slide 9

Slide 9 text

CQRS - Event Sourcing • "It is simply the creation of two objects where there was previously only one" - Greg Young • Write Model receives Commands and produces Events. • Read Models are created from Events.

Slide 10

Slide 10 text

CQRS - Event Sourcing • "It is simply the creation of two objects where there was previously only one" - Greg Young • Write Model receives Commands and produces Events. • Read Models are created from Events. • Events can be stored and replayed.

Slide 11

Slide 11 text

Event Driven / Event Sourcing • CQRS is Event Driven, but not necessarily implements Event Sourcing

Slide 12

Slide 12 text

Event Driven / Event Sourcing • CQRS is Event Driven, but not necessarily implements Event Sourcing • in synchronous CQRS:

Slide 13

Slide 13 text

Event Driven / Event Sourcing • CQRS is Event Driven, but not necessarily implements Event Sourcing • in synchronous CQRS: •tx(Cmd 㱺 Write Model 㱺 Event 㱺 View)

Slide 14

Slide 14 text

Event Driven / Event Sourcing • CQRS is Event Driven, but not necessarily implements Event Sourcing • in synchronous CQRS: •tx(Cmd 㱺 Write Model 㱺 Event 㱺 View) •tx(Cmd 㱺 Write Model 㱺 Event 㱺 
 View1, View2, View3, View4, …, ViewN)

Slide 15

Slide 15 text

Event Driven / Event Sourcing • CQRS is Event Driven, but not necessarily implements Event Sourcing • in synchronous CQRS: •tx(Cmd 㱺 Write Model 㱺 Event 㱺 View) •tx(Cmd 㱺 Write Model 㱺 Event 㱺 
 View1, View2, View3, View4, …, ViewN) Won’t scale! Damm blocking!

Slide 16

Slide 16 text

Event Driven / Event Sourcing • in asynchronous CQRS: •tx(Cmd 㱺 Write Model 㱺 Event) •tx(Event 㱺 View1) •tx(Event 㱺 View2) •tx(Event 㱺 View3)

Slide 17

Slide 17 text

Event Driven / Event Sourcing • in asynchronous CQRS: •tx(Cmd 㱺 Write Model 㱺 Event) •tx(Event 㱺 View1) •tx(Event 㱺 View2) •tx(Event 㱺 View3) • Introduces Eventual Consistency

Slide 18

Slide 18 text

• Aggregate responsible for consistency • You can only modify one Aggregate per transaction • Write Model receives Commands and produces Events • Read Models are created from Events • Events can be stored and replayed (when Event Sourcing) • Eventual Consistency (if async Write/Read models)

Slide 19

Slide 19 text

Basic Operations (non-reactive) trait Cmd trait Event trait Aggregate // validate a Command and produces an Event def validate(cmd:Cmd): Event // apply creational Event and produces a new Aggregate def applyEvent(evt:Event): Aggregate // validate a Command against current Aggregate state def validate(agg:Aggregate, cmd:Cmd): Seq[Event] // apply event on current Aggregate def applyEvent(agg:Aggregate, evt:Event): Aggregate

Slide 20

Slide 20 text

Basic Operations (non-reactive) trait Cmd trait Event trait Aggregate // validate a Command and produces an Event def validate(cmd:Cmd): Event // apply creational Event and produces a new Aggregate def applyEvent(evt:Event): Aggregate // validate a Command against current Aggregate state def validate(agg:Aggregate, cmd:Cmd): Seq[Event] // apply event on current Aggregate def applyEvent(agg:Aggregate, evt:Event): Aggregate

Slide 21

Slide 21 text

Basic Operations (non-reactive) trait Cmd trait Event trait Aggregate // validate a Command and produces an Event def validate(cmd:Cmd): Event // apply creational Event and produces a new Aggregate def applyEvent(evt:Event): Aggregate // validate a Command against current Aggregate state def validate(agg:Aggregate, cmd:Cmd): Seq[Event] // apply event on current Aggregate def applyEvent(agg:Aggregate, evt:Event): Aggregate

Slide 22

Slide 22 text

Basic Operations (non-reactive) trait Cmd trait Event trait Aggregate // validate a Command and produces an Event def validate(cmd:Cmd): Event // apply creational Event and produces a new Aggregate def applyEvent(evt:Event): Aggregate // validate a Command against current Aggregate state def validate(agg:Aggregate, cmd:Cmd): Seq[Event] // apply event on current Aggregate def applyEvent(agg:Aggregate, evt:Event): Aggregate

Slide 23

Slide 23 text

State of Aggregate Event Store

Slide 24

Slide 24 text

E0 State of Aggregate Event Store // first command is validated def validate(cmd:Cmd): Event // CreateOrder => OrderCreated

Slide 25

Slide 25 text

E0 State of Aggregate Event Store S0 // first command is validated def validate(cmd:Cmd): Event // CreateOrder => OrderCreated // apply creational Event and produces a new Aggregate def applyEvent(evt:Event): Aggregate // OrderCreated => Order

Slide 26

Slide 26 text

E0 State of Aggregate Event Store E1 S0 // validate a Command against current Aggregate state def validate(agg:Aggregate, cmd:Cmd): Seq[Event] // (Order, AddProduct) => ProductAdded

Slide 27

Slide 27 text

E0 State of Aggregate Event Store E1 S0 S1 // apply event on current Aggregate def applyEvent(agg:Aggregate, evt:Event): Aggregate // (Order, ProductAdded) => Order // validate a Command against current Aggregate state def validate(agg:Aggregate, cmd:Cmd): Seq[Event] // (Order, AddProduct) => ProductAdded

Slide 28

Slide 28 text

E0 State of Aggregate Event Store E1 E2 S0 S1 // validate a Command against current Aggregate state def validate(agg:Aggregate, cmd:Cmd): Seq[Event] // (Order, ExecuteOrder) => OrderExecute

Slide 29

Slide 29 text

E0 State of Aggregate Event Store E1 E2 S0 S1 S2 // validate a Command against current Aggregate state def validate(agg:Aggregate, cmd:Cmd): Seq[Event] // (Order, ExecuteOrder) => OrderExecute // apply event on current Aggregate def applyEvent(agg:Aggregate, evt:Event): Aggregate // (Order, OrderExecuted) => Order

Slide 30

Slide 30 text

E0 State of Aggregate Event Store E1 E2 S0 S1 S2 // validate a Command against current Aggregate state def validate(agg:Aggregate, cmd:Cmd): Seq[Event] // (Order, ExecuteOrder) => OrderExecute E3

Slide 31

Slide 31 text

E0 State of Aggregate Event Store E1 E2 S0 S1 S2 // validate a Command against current Aggregate state def validate(agg:Aggregate, cmd:Cmd): Seq[Event] // (Order, ExecuteOrder) => OrderExecute E3 Invalid Command! Order is already executed!

Slide 32

Slide 32 text

Watch out! We don’t throw exceptions in Scala. We’ll fix it soon! E0 State of Aggregate Event Store E1 E2 S0 S1 S2 // validate a Command against current Aggregate state def validate(agg:Aggregate, cmd:Cmd): Seq[Event] // (Order, ExecuteOrder) => OrderExecute E3 Invalid Command! Order is already executed!

Slide 33

Slide 33 text

E0 State of Aggregate Event Store E1 E2

Slide 34

Slide 34 text

E0 State of Aggregate Event Store E1 E2 E0 S0 // apply creational Event and produces a new Aggregate // OrderCreated => Order def applyEvent(evt:Event): Aggregate

Slide 35

Slide 35 text

E0 State of Aggregate Event Store E1 E2 S0 E0 S0 // apply creational Event and produces a new Aggregate // OrderCreated => Order def applyEvent(evt:Event): Aggregate

Slide 36

Slide 36 text

E0 State of Aggregate Event Store E1 E2 S0 S0 S1 // apply event on current Aggregate // (Order, ProductAdded) => Order def applyEvent(agg:Aggregate, evt:Event): Aggregate E1

Slide 37

Slide 37 text

E0 State of Aggregate Event Store E1 E2 S0 S1 S0 S1 // apply event on current Aggregate // (Order, ProductAdded) => Order def applyEvent(agg:Aggregate, evt:Event): Aggregate E1

Slide 38

Slide 38 text

E0 State of Aggregate Event Store E1 E2 S0 S1 E2 S1 S2 // apply event on current Aggregate // (Order, OrderExecuted) => Order def applyEvent(agg:Aggregate, evt:Event): Aggregate

Slide 39

Slide 39 text

E0 State of Aggregate Event Store E1 E2 S0 S1 S2 E2 S1 S2 // apply event on current Aggregate // (Order, OrderExecuted) => Order def applyEvent(agg:Aggregate, evt:Event): Aggregate

Slide 40

Slide 40 text

CQRS using Scala std lib • DomainCommand and DomainEvent • A Protocol - message (cmd & evt) for a given Aggregate • A case class extending the Aggregate trait • A Behavior definition - Reactive

Slide 41

Slide 41 text

import java.util.UUID trait DomainCommand { val id: CommandId = CommandId() } case class CommandId(value: UUID = UUID.randomUUID()) trait DomainEvent { def id: EventId } case class EventId(value: UUID = UUID.randomUUID())

Slide 42

Slide 42 text

object ProductProtocol extends ProtocolDef { sealed trait ProductCommand extends ProtocolCommand sealed trait ProductEvent extends ProtocolEvent } trait ProtocolDef { trait ProtocolCommand extends DomainCommand trait ProtocolEvent extends DomainEvent }

Slide 43

Slide 43 text

object ProductProtocol extends ProtocolDef { sealed trait ProductCommand extends ProtocolCommand sealed trait ProductEvent extends ProtocolEvent case class CreateProduct(name: String, description: String, price: Double) extends ProductCommand case class ProductCreated(name: String, description: String, price: Double, id: EventId = EventId()) extends ProductEvent } trait ProtocolDef { trait ProtocolCommand extends DomainCommand trait ProtocolEvent extends DomainEvent }

Slide 44

Slide 44 text

trait ProtocolDef { trait ProtocolCommand extends DomainCommand trait ProtocolEvent extends DomainEvent } object ProductProtocol extends ProtocolDef { sealed trait ProductCommand extends ProtocolCommand sealed trait ProductEvent extends ProtocolEvent case class CreateProduct(name: String, description: String, price: Double) extends ProductCommand case class ProductCreated(name: String, description: String, price: Double, id: EventId = EventId()) extends ProductEvent case class ChangePrice(price: Double) extends ProductCommand case class PriceChanged(newPrice: Double, id: EventId = EventId()) extends ProductEvent }

Slide 45

Slide 45 text

trait AggregateID { def value: String } trait Aggregate { type Id <: AggregateID type Protocol <: ProtocolDef def id: Id } case class ProductNumber(value: String) extends AggregateID case class Product(name: String, description: String, price: Double, id: ProductNumber) extends Aggregate { type Id = ProductNumber type Protocol = ProductProtocol.type }

Slide 46

Slide 46 text

trait Behavior[A <: Aggregate] { type AggregateType = A type Command = A#Protocol#ProtocolCommand type Event = A#Protocol#ProtocolEvent type Events = Seq[Event] def applyEvent(event: Event): AggregateType def applyEvent(event: Event, aggregate: AggregateType): AggregateType // async behavior protected def validateAsync(cmd: Command) (implicit ec: ExecutionContext): Future[Event] protected def validateAsync(cmd: Command, aggregate: AggregateType) (implicit ec: ExecutionContext): Future[Events] }

Slide 47

Slide 47 text

trait Behavior[A <: Aggregate] { type AggregateType = A type Command = A#Protocol#ProtocolCommand type Event = A#Protocol#ProtocolEvent type Events = Seq[Event] def applyEvent(event: Event): AggregateType def applyEvent(event: Event, aggregate: AggregateType): AggregateType // async behavior protected def validateAsync(cmd: Command) (implicit ec: ExecutionContext): Future[Event] protected def validateAsync(cmd: Command, aggregate: AggregateType) (implicit ec: ExecutionContext): Future[Events] }

Slide 48

Slide 48 text

trait Behavior[A <: Aggregate] { type AggregateType = A type Command = A#Protocol#ProtocolCommand type Event = A#Protocol#ProtocolEvent type Events = Seq[Event] def applyEvent(event: Event): AggregateType def applyEvent(event: Event, aggregate: AggregateType): AggregateType // async behavior protected def validateAsync(cmd: Command) (implicit ec: ExecutionContext): Future[Event] protected def validateAsync(cmd: Command, aggregate: AggregateType) (implicit ec: ExecutionContext): Future[Events] }

Slide 49

Slide 49 text

trait Behavior[A <: Aggregate] { type AggregateType = A type Command = A#Protocol#ProtocolCommand type Event = A#Protocol#ProtocolEvent type Events = Seq[Event] def applyEvent(event: Event): AggregateType def applyEvent(event: Event, aggregate: AggregateType): AggregateType // async behavior protected def validateAsync(cmd: Command) (implicit ec: ExecutionContext): Future[Event] protected def validateAsync(cmd: Command, aggregate: AggregateType) (implicit ec: ExecutionContext): Future[Events] } Inconvenient to always have to work with Futures and Seqs

Slide 50

Slide 50 text

// on-create Command => Event Command => Future[Event] // post-create (Command, AggregateType) => Event (Command, AggregateType) => Future[Event] (Command, AggregateType) => Seq[Event] (Command, AggregateType) => Future[Seq[Event]] We need some variations, for instance…

Slide 51

Slide 51 text

// on-create Command => Event Command => Future[Event] // post-create (Command, AggregateType) => Event (Command, AggregateType) => Future[Event] (Command, AggregateType) => Seq[Event] (Command, AggregateType) => Future[Seq[Event]] We need some variations, for instance…

Slide 52

Slide 52 text

PartialFunction[A,B] a1 a3 a2 b1 b2 b3 domain = a1, a2 and a3 a4

Slide 53

Slide 53 text

PartialFunction[A, Option[B]] a1 a3 a2 Some(b1) Some(b2) Some(b3) domain = a1, a2 and a3 a4 None def fallback[A, B]: PartialFunction[A, Option[B]] = { case any => None }

Slide 54

Slide 54 text

def fallback[A, B]: PartialFunction[A, Future[B]] = { case any => case any => Future.failed(new IllegalArgumentException(s"Can NOT do anything useful with $any")) } PartialFunction[A, Future[B]] a1 a3 a2 Success(b1) Success(b2) Success(b3) domain = a1, a2 and a3 a4 Failure(e)

Slide 55

Slide 55 text

Behavior DSL • PartialFunctions for all results we want to produce
 (Event, Future[Event], Seq[Event] and Future[Seq[Event]])

Slide 56

Slide 56 text

Behavior DSL • PartialFunctions for all results we want to produce
 (Event, Future[Event], Seq[Event] and Future[Seq[Event]]) • lift them all to Future[Event] or Future[Seq[Event]]

Slide 57

Slide 57 text

Behavior DSL • PartialFunctions for all results we want to produce
 (Event, Future[Event], Seq[Event] and Future[Seq[Event]]) • lift them all to Future[Event] or Future[Seq[Event]] • fallback PartialFunction for unknown Commands
 returning a failed Future

Slide 58

Slide 58 text

Behavior DSL • PartialFunctions for all results we want to produce
 (Event, Future[Event], Seq[Event] and Future[Seq[Event]]) • lift them all to Future[Event] or Future[Seq[Event]] • fallback PartialFunction for unknown Commands
 returning a failed Future • compose them all with ‘orElse’ (fallback as last)

Slide 59

Slide 59 text

Behavior DSL • PartialFunctions for all results we want to produce
 (Event, Future[Event], Seq[Event] and Future[Seq[Event]]) • lift them all to Future[Event] or Future[Seq[Event]] • fallback PartialFunction for unknown Commands
 returning a failed Future • compose them all with ‘orElse’ (fallback as last) • Since Future has two possible outcomes (Success and Failure)
 we can build the total functions we need

Slide 60

Slide 60 text

def behavior(num: ProductNumber): Behavior[Product] = { // DSL to build a Behavior using PF behaviorFor[Product].whenConstructing { it => it.emitsEvent { // PF (Command) => Event case cmd: CreateProduct if cmd.price > 0 => ProductCreated(cmd.name, cmd.description, cmd.price) } it.acceptsEvents { // PF (Event) => Aggregate case evt: ProductCreated => Product(evt.name, evt.description, evt.price, num) } }.whenUpdating { it => it.rejectsCommands { // PF (Aggregate, Command) => Throwable case (prod, cmd: ChangePrice) if cmd.price < prod.price => new CommandException("Can't decrease the price") } it.emitsSingleEvent { // PF (Aggregate, Command) => Event case (_, cmd: ChangePrice) if cmd.price > 0 => PriceChanged(cmd.price) }

Slide 61

Slide 61 text

def behavior(num: ProductNumber): Behavior[Product] = { // DSL to build a Behavior using PF behaviorFor[Product].whenConstructing { it => it.emitsEvent { // PF (Command) => Event case cmd: CreateProduct if cmd.price > 0 => ProductCreated(cmd.name, cmd.description, cmd.price) } it.acceptsEvents { // PF (Event) => Aggregate case evt: ProductCreated => Product(evt.name, evt.description, evt.price, num) } }.whenUpdating { it => it.rejectsCommands { // PF (Aggregate, Command) => Throwable case (prod, cmd: ChangePrice) if cmd.price < prod.price => new CommandException("Can't decrease the price") } it.emitsSingleEvent { // PF (Aggregate, Command) => Event case (_, cmd: ChangePrice) if cmd.price > 0 => PriceChanged(cmd.price) }

Slide 62

Slide 62 text

case evt: ProductCreated => Product(evt.name, evt.description, evt.price, num) } }.whenUpdating { it => it.rejectsCommands { // PF (Aggregate, Command) => Throwable case (prod, cmd: ChangePrice) if cmd.price < prod.price => new CommandException("Can't decrease the price") } it.emitsSingleEvent { // PF (Aggregate, Command) => Event case (_, cmd: ChangePrice) if cmd.price > 0 => PriceChanged(cmd.price) } it.acceptsEvents { // PF (Aggregate, Event) => Aggregate case (prod, evt: PriceChanged) => prod.copy(price = evt.newPrice) } } }

Slide 63

Slide 63 text

Akka for Event Sourcing • Given an Aggregate, its Protocol and its Behavior…

Slide 64

Slide 64 text

Akka for Event Sourcing • Given an Aggregate, its Protocol and its Behavior… • …we can have an AggregateActor that understands the life-cycle of an Aggregate

Slide 65

Slide 65 text

Akka for Event Sourcing • Given an Aggregate, its Protocol and its Behavior… • …we can have an AggregateActor that understands the life-cycle of an Aggregate • Events are stored and replayed via Akka Persistence

Slide 66

Slide 66 text

Akka for Event Sourcing • Given an Aggregate, its Protocol and its Behavior… • …we can have an AggregateActor that understands the life-cycle of an Aggregate • Events are stored and replayed via Akka Persistence • One Actor per Aggregate Id

Slide 67

Slide 67 text

Akka for Event Sourcing • Given an Aggregate, its Protocol and its Behavior… • …we can have an AggregateActor that understands the life-cycle of an Aggregate • Events are stored and replayed via Akka Persistence • One Actor per Aggregate Id • Actor MUST handle one single Command at time (become/unbecome)

Slide 68

Slide 68 text

Akka for Event Sourcing • Given an Aggregate, its Protocol and its Behavior… • …we can have an AggregateActor that understands the life-cycle of an Aggregate • Events are stored and replayed via Akka Persistence • One Actor per Aggregate Id • Actor MUST handle one single Command at time (become/unbecome) • Guarantees consistency without Optimistic Locking

Slide 69

Slide 69 text

AggregateActor Behavior // initialized with Aggregate’s Id and Behavior class AggregateActor[A <: Aggregate](identifier: A#Id, behavior: Behavior[A]) extends PersistentActor Event Store

Slide 70

Slide 70 text

AggregateActor Behavior // initialized with Aggregate’s Id and Behavior class AggregateActor[A <: Aggregate](identifier: A#Id, behavior: Behavior[A]) extends PersistentActor Event Store CreateProduct receives first command

Slide 71

Slide 71 text

AggregateActor Behavior // initialized with Aggregate’s Id and Behavior class AggregateActor[A <: Aggregate](identifier: A#Id, behavior: Behavior[A]) extends PersistentActor Event Store E0 Behavior emits first Event Actor pesists it ProductCreated

Slide 72

Slide 72 text

AggregateActor Behavior // initialized with Aggregate’s Id and Behavior class AggregateActor[A <: Aggregate](identifier: A#Id, behavior: Behavior[A]) extends PersistentActor Event Store E0 Behavior applies Event and constructs Aggregate Aggregate ProductCreated

Slide 73

Slide 73 text

AggregateActor Behavior // initialized with Aggregate’s Id and Behavior class AggregateActor[A <: Aggregate](identifier: A#Id, behavior: Behavior[A]) extends PersistentActor ChangePrice receives an update Event Store E0 Aggregate

Slide 74

Slide 74 text

AggregateActor Behavior // initialized with Aggregate’s Id and Behavior class AggregateActor[A <: Aggregate](identifier: A#Id, behavior: Behavior[A]) extends PersistentActor Event Store E1 E0 Aggregate Behavior emits Event Actor pesists it PriceChanged

Slide 75

Slide 75 text

AggregateActor Behavior // initialized with Aggregate’s Id and Behavior class AggregateActor[A <: Aggregate](identifier: A#Id, behavior: Behavior[A]) extends PersistentActor Event Store E1 E0 Aggregate PriceChanged Behavior applies Event to Aggregate

Slide 76

Slide 76 text

View Projections • Akka Persistence Query (experimental in Akka 2.4.0)

Slide 77

Slide 77 text

View Projections • Akka Persistence Query (experimental in Akka 2.4.0) • Generate Read Model (Views) from Events

Slide 78

Slide 78 text

View Projections • Akka Persistence Query (experimental in Akka 2.4.0) • Generate Read Model (Views) from Events • Reactive Stream of Events

Slide 79

Slide 79 text

View Projections • Akka Persistence Query (experimental in Akka 2.4.0) • Generate Read Model (Views) from Events • Reactive Stream of Events • ProjectionActor is an ActorSubscriber (backpressure)

Slide 80

Slide 80 text

No content