Top Banner
@crichardson Developing functional domain models with event sourcing Chris Richardson Author of POJOs in Action Founder of the original CloudFoundry.com Founder of a microservices platform startup @crichardson [email protected] http://plainoldobjects.com http://microservices.io
65

Developing functional domain models with event sourcing (sbtb, sbtb2015)

Jan 10, 2017

Download

Software

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Developing functional domain models with event sourcingChris Richardson

Author of POJOs in Action Founder of the original CloudFoundry.com Founder of a microservices platform startup

@crichardson [email protected] http://plainoldobjects.com http://microservices.io

Page 2: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Presentation goal

How to design functional domain models using event sourcing?

Page 3: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

About Chris

Page 4: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

About Chris

Consultant and trainer focusing on microservices (http://www.chrisrichardson.net/)

Page 5: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

About Chris

Founder of a startup that is creating a platform that makes it easy for

application developers write microservices

(http://bit.ly/trialeventuate)

Page 6: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

For more information

https://github.com/cer/event-sourcing-examples

http://microservices.io

http://plainoldobjects.com/

https://twitter.com/crichardson

Page 7: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Agenda

Why event sourcing?

Designing a domain model based on event sourcing

Event sourcing and service design

Page 8: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Tomcat

Traditional monolithic architecture

Browser/Client

WAR/EAR

RDBMS

Customers

Accounts

Transfers

Banking UI

develop test

deploy

Simple to

Load balancer

scale

Spring MVC

Spring Hibernate

...

HTML

REST/JSON

ACID

Page 9: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

But that leads* to monolithic hell

For large and/or complex applications…

Page 10: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Today: use a microservice, polyglot architecture

Banking UI

Account Management Service MoneyTransfer Management Service

Account Database MoneyTransfer Database

Standalone services

Sharded SQLNoSQL DB

Page 11: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

But now we have distributed data

management problems

Page 12: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Customer management

Maintaining invariants across service boundaries

Order management

Order Service

placeOrder()

Customer Service

updateCreditLimit()

Customer

creditLimit ...

has ordersbelongs toOrder

total

Invariant: sum(open order.total) <= customer.creditLimit

?

Page 13: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Maintaining invariants with (non-ACID) NoSQL databases

Non-ACID NoSQL database

Order Service

placeOrder()

Customer Service

updateCreditLimit()

Customer

creditLimit ...

has ordersbelongs toOrder

total

Invariant: sum(open order.total) <= customer.creditLimit

?

Page 14: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Use an event-driven architecture

Services publish events when state changes

Services subscribe to events and update their state

Maintain eventual consistency across multiple aggregates (in multiple datastores)

Synchronize replicated data

Page 15: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Order ManagementOrder

id : 4567 total: 343 state = CREATED

Customer Management

Customer creditLimit : 12000 creditReservations: {}

Customer creditLimit : 12000 creditReservations: { 4567 -> 343}

Order id : 4567 total: 343 state = OPEN

Eventually consistent credit checking

Message Bus

createOrder()

Publishes:Subscribes to:

Subscribes to:

publishes:

OrderCreatedEvent

CreditReservedEvent

OrderCreatedEvent CreditReservedEvent

Page 16: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

How to atomically

update the database and

publish events without 2PC?

(dual write problem)

Page 17: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Update the database and

Publish events

Page 18: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Event sourcingFor each aggregate (aka. business entity):

Identify (state-changing) domain events

Define Event classes

For example,

Account: AccountOpenedEvent, AccountDebitedEvent, AccountCreditedEvent

ShoppingCart: ItemAddedEvent, ItemRemovedEvent, OrderPlacedEvent

Page 19: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Persists events NOT current state

Account

balance

open(initial) debit(amount) credit(amount)

AccountOpened

Event table

AccountCredited

AccountDebited

101 450

Account tableX101

101

101

901

902

903

500

250

300

Page 20: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Replay events to recreate state

Account

balance

AccountOpenedEvent(balance) AccountDebitedEvent(amount) AccountCreditedEvent(amount)

Events

Periodically snapshot to avoid loading all events

Page 21: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

The present is a fold over history

Page 22: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Event

Aggregates: Command => Events

AggregateCommand Event

Page 23: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Request handling in an event-sourced application

HTTP Handler

Event Store

pastEvents = findEvents(entityId)

Account

new()

applyEvents(pastEvents)

newEvents = processCmd(SomeCmd)

saveEvents(newEvents)

Microservice A

(optimistic locking)

Page 24: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Event Store publishes events - consumed by other services

Event Store

Event Subscriber

subscribe(EventTypes)

publish(event)

publish(event)

Aggregate

CQRS View

update()

update()

Microservice B

send notifications

Page 25: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Benefits of event sourcingSolves data consistency issues in a Microservice/NoSQL-based architecture

Reliable event publishing: publishes events needed by predictive analytics etc, user notifications,…

Eliminates O/R mapping problem (mostly)

Reifies state changes:

Built-in, reliable audit log,

temporal queries

Preserved history ⇒ More easily implement future requirements

Page 26: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Drawbacks of event sourcing

Weird and unfamiliar

Events = a historical record of your bad design decisions

Handling duplicate events can be tricky

Application must handle eventually consistent data

Event store only directly supports PK-based lookup => use Command Query Responsibility Segregation (CQRS) to handle queries

Page 27: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Agenda

Why event sourcing?

Designing a domain model based on event sourcing

Event sourcing and service design

Page 28: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Use the familiar building blocks of DDD

Entity

Value object

Services

Repositories

Aggregates

Page 29: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Partition the domain model into Aggregates

Order

OrderLine Item

quantity

Addressstreet city …

Customer

Productname price

Page 30: Developing functional domain models with event sourcing (sbtb, sbtb2015)

Aggregate designGraph consisting of a root entity and one or more other entities and value objects

Each core business entity = Aggregate: e.g. customer, Account, Order, Product, ….

Reference other aggregate roots via primary key

Often contains partial copy of other aggregates’ data

Order

OrderLine Item

quantity productId productName productPrice

customerId

Address

street city …

Page 31: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Modular business logic = flexible deployment

Tomcat

WAR/EAR

Customer

Order

Tomcat

WAR/EAR

Customer

Tomcat

WAR/EAR

Order

OR

Page 32: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Aggregate granularity is important

Transaction = processing one command by one aggregate

No opportunity to update multiple aggregates within a transaction

If an update must be atomic (i.e. no compensating transaction) then it must be handled by a single aggregate

e.g. scanning boarding pass at security checkpoint or when entering jetway

Page 33: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Aggregate granularity

Customer

Product

Order

Customer

Product

Order

ConsistencyDecomposability/

Scalability/ User experience

Page 34: Developing functional domain models with event sourcing (sbtb, sbtb2015)

Designing domain eventsRecords state changes for an aggregate

Records key “business events”

Part of the public API of the domain model ProductAddedToCart

id : TimeUUID productId productName productPrice shoppingCartId

Required by aggregate

Enrichment: Required by consumers

Page 35: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Designing commandsCreated by a service from incoming request

Processed by an aggregate

Immutable

Contains value objects for

Validating request

Creating event

Auditing user activity

Page 36: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Hybrid OO/FP domain objects

Page 37: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

OO = State + Behavior

creditLimit creditReservations

Customer

processCommand : PartialFunction[Command, Events]

applyEvent : PartialFunction[Event, Account]

State

Behavior

Page 38: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Aggregate traits

Map Command to Events

Apply event returning updated Aggregate

Used by Event Store

to reconstitute aggregate

Event types are not precisely typed :-(

Page 39: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Customer - command processing

Check available credit

Page 40: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Customer - applying events

Immutable

Page 41: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Event Store APItrait EventStore {

def save[T <: Aggregate[T]](entity: T, events: Seq[Event], assignedId : Option[EntityId] = None): Future[EntityWithIdAndVersion[T]]

def update[T <: Aggregate[T]](entityIdAndVersion : EntityIdAndVersion, entity: T, events: Seq[Event]): Future[EntityWithIdAndVersion[T]]

def find[T <: Aggregate[T] : ClassTag](entityId: EntityId) : Future[EntityWithIdAndVersion[T]]

def findOptional[T <: Aggregate[T] : ClassTag](entityId: EntityId) Future[Option[EntityWithIdAndVersion[T]]]

def subscribe(subscriptionId: SubscriptionId): Future[AcknowledgableEventStream] }

Page 42: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Unsatisfying design with imprecise typing

https://flic.kr/p/2Bh518

“I hate OO”

Page 43: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

A detour to Haskell

http://www.meetup.com/oakland-scala/Oakland Advanced Scala Study Group

https://www.flickr.com/photos/georgikeith/2658949664

Page 44: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Haskell event sourcing aggregate…

https://gist.github.com/Fristi/7327904

Associated data family ~ abstract type members

The operations

Page 45: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

…Haskell Aggregate

Page 46: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Haskell TicTacToe aggregateConcrete type definitions

Command Processing

Apply Event

Page 47: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Functional event sourcing in Scala

Page 48: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

“This section briefly explains GHC Haskell’s associated types and shows in detail how they can be encoded in Scala using

type members …”

Page 49: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

FP = Separation of State and Behavior

Customer

creditLimit …

CustomerAggregate

processCommand(Account, Command) : Seq[Events]

applyEvent(Account, Event) : Account

State Behavior

Page 50: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Aggregate type classesUsed by

Event Store to

reconstitute aggregates

Hardwired

Page 51: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Customer Aggregate….State

Behavior

Page 52: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

…command processing…

Page 53: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

… applying events

Page 54: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Using Lenses with aggregates

Page 55: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

FP-style Event Store API

Aggregate type class

Page 56: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Agenda

Why event sourcing?

Designing a domain model based on event sourcing

Event sourcing and service design

Page 57: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardsonEvent Store

HTTP Request

HTTP Adapter

Event Handler

Cmd

Cmd

Events

Events

Xyz Adapter

Xyz Requestmicroservice

Aggregate

Service

Event Adapter

Page 58: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Place Order example

As a customer I want to place an order So that I get the needed products

Given that my available credit is $1500 When I place a $250 order Then the order is created Then my available credit is $1250

Story

Scenario

Post conditions

Pre conditions

Page 59: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Old-style ACID…BEGIN TRANSACTION

… RESERVE CREDIT …

… CREATE ORDER…

COMMIT TRANSACTION

Page 60: Developing functional domain models with event sourcing (sbtb, sbtb2015)

… becomes eventually consistent (BASE)

Updating multiple aggregates

multi-step, event-driven flow

each step updates one Aggregate

Service creates saga to coordinate workflow

A state machine

Part of the domain, e.g. Order aggregate OR Synthetic aggregate

Post-conditions eventually true

Order

Customer

CreatedCredit reserved

public Order createOrder() { … Creates Order … }

Approved

Page 61: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Need compensating transactions

Pre-conditions might be false when attempting to update an aggregate

Credit check might fail => cancel order

Credit check succeeded but customer cancels order => undo credit reservation

Page 62: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Creating an order

DSL concisely specifies: 1.Creates Customer aggregate 2.Processes command 3.Applies events 4.Persists events

Page 63: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

Event handling in AccountDurable subscription nameTriggers BeanPostProcessor

1.Load Customer aggregate 2.Processes command 3.Applies events 4.Persists events

Page 64: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

SummaryEvent sourcing solves a variety of problems in modern application architectures

ES-based architecture = choice of monolith or microservices

Scala is a great language for implementing ES-based domain models:

Case classes

Pattern matching

Recreating state = functional fold over events

Page 65: Developing functional domain models with event sourcing (sbtb, sbtb2015)

@crichardson

@crichardson [email protected]

http://plainoldobjects.com http://microservices.io http://bit.ly/trialeventuate