developing microservices with aggregates (devnexus2017)

79
@crichardson Developing Microservices with Aggregates Chris Richardson Founder of Eventuate.io Founder of the original CloudFoundry.com Author of POJOs in Action @crichardson [email protected] http://eventuate.io

Upload: chris-richardson

Post on 19-Mar-2017

672 views

Category:

Software


2 download

TRANSCRIPT

Page 1: Developing microservices with aggregates (devnexus2017)

@crichardson

Developing Microservices with Aggregates

Chris Richardson

Founder of Eventuate.io Founder of the original CloudFoundry.com Author of POJOs in Action

@crichardson [email protected] http://eventuate.io

Page 2: Developing microservices with aggregates (devnexus2017)

@crichardson

Presentation goal

Show how Domain Driven Design Aggregates

and Microservices are a perfect match

Page 3: Developing microservices with aggregates (devnexus2017)

@crichardson

About Chris

Page 4: Developing microservices with aggregates (devnexus2017)

@crichardson

About Chris

Consultant and trainer focusing on modern

application architectures including microservices

(http://www.chrisrichardson.net/)

Page 5: Developing microservices with aggregates (devnexus2017)

@crichardson

About Chris

Founder of a startup that is creating an open-source/SaaS platform

that simplifies the development of transactional microservices

(http://eventuate.io)

Page 6: Developing microservices with aggregates (devnexus2017)

@crichardson

For more information

http://learnmicroservices.io

Page 7: Developing microservices with aggregates (devnexus2017)

@crichardson

Agenda

The problem with Domain Models and microservices

Overview of aggregates

Maintaining consistency between aggregates

Using event sourcing with Aggregates

Customers and Orders example

Using explicit saga orchestration

Page 8: Developing microservices with aggregates (devnexus2017)

@crichardson

The Microservice architecture tackles complexity through

modularization

Page 9: Developing microservices with aggregates (devnexus2017)

@crichardson

Microservice =

Business capability

Page 10: Developing microservices with aggregates (devnexus2017)

@crichardson

Microservice architecture

Browser

Mobile Device

Store Front UI

API Gateway

Catalog Service

Review Service

Order Service

… Service

Catalog Database

Review Database

Order Database

… Database

HTML

REST

REST

Database per service

Page 11: Developing microservices with aggregates (devnexus2017)

@crichardson

But there are challenges…

Page 12: Developing microservices with aggregates (devnexus2017)

@crichardson

Product service

Customer serviceOrder service

Domain model = tangled web of classes

Order

OrderLine Item

quantity

Addressstreet city …

Customer

Productname price

?

?

creditLimit

Page 13: Developing microservices with aggregates (devnexus2017)

@crichardson

Reliance on ACID transactions to enforce invariantsBEGIN TRANSACTION … SELECT ORDER_TOTAL FROM ORDERS WHERE CUSTOMER_ID = ? … SELECT CREDIT_LIMIT FROM CUSTOMERS WHERE CUSTOMER_ID = ? … INSERT INTO ORDERS … … COMMIT TRANSACTION

Simple and ACID

Page 14: Developing microservices with aggregates (devnexus2017)

@crichardson

But it violates encapsulation…BEGIN TRANSACTION … SELECT ORDER_TOTAL FROM ORDERS WHERE CUSTOMER_ID = ? … SELECT CREDIT_LIMIT FROM CUSTOMERS WHERE CUSTOMER_ID = ? … INSERT INTO ORDERS … … COMMIT TRANSACTION

Private to the Order Service

Private to the Customer Service

Page 15: Developing microservices with aggregates (devnexus2017)

@crichardson

.. and requires 2PCBEGIN TRANSACTION … SELECT ORDER_TOTAL FROM ORDERS WHERE CUSTOMER_ID = ? … SELECT CREDIT_LIMIT FROM CUSTOMERS WHERE CUSTOMER_ID = ? … INSERT INTO ORDERS … … COMMIT TRANSACTION

Page 16: Developing microservices with aggregates (devnexus2017)

@crichardson

2PC is not an optionGuarantees consistency

BUT

2PC coordinator is a single point of failure

Chatty: at least O(4n) messages, with retries O(n^2)

Reduced throughput due to locks

Not supported by many NoSQL databases (or message brokers)

CAP theorem ⇒ 2PC impacts availability

….

Page 17: Developing microservices with aggregates (devnexus2017)

@crichardson

Doesn’t fit with the NoSQL DB “transaction” model

Page 18: Developing microservices with aggregates (devnexus2017)

@crichardson

Agenda

The problem with Domain Models and microservices

Overview of aggregates

Maintaining consistency between aggregates

Using event sourcing with Aggregates

Customers and Orders example

Using explicit saga orchestration

Page 19: Developing microservices with aggregates (devnexus2017)

@crichardson

Domain Driven Design - building blocks

Entity

Value object

Services

Repositories

Aggregates

Adopted

Ignored (at least by me)

Page 20: Developing microservices with aggregates (devnexus2017)

About AggregatesCluster of objects that can be treated as a unit

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

Typically business entities are Aggregates, e.g. customer, Account, Order, Product, ….

Order

OrderLine Item

quantity productId productName productPrice

customerId

Address

street city …

Page 21: Developing microservices with aggregates (devnexus2017)

Aggregate: rule #1

Reference other aggregate roots via

identity

(primary key)

Order

OrderLine Item

quantity productId productName productPrice

customerId

Address

street city …

Page 22: Developing microservices with aggregates (devnexus2017)

@crichardson

Foreign keys in a Domain Model?!?

Page 23: Developing microservices with aggregates (devnexus2017)

@crichardson

Domain model = collection of loosely connected aggregates

Order

OrderLine Item

quantity

Addressstreet city …

Customer

Productname price

Page 24: Developing microservices with aggregates (devnexus2017)

@crichardson

Product service

Customer serviceOrder service

Easily partition into microservices

Order

OrderLine Item

quantity

Addressstreet city …

Customer

Productname price

Page 25: Developing microservices with aggregates (devnexus2017)

@crichardson

Aggregate rule #2

Transaction =

processing one command by one aggregate

Page 26: Developing microservices with aggregates (devnexus2017)

@crichardson

Product service

Customer serviceOrder service

Transaction scope = service

Order

OrderLine Item

quantity

Addressstreet city …

Customer

Productname price

Page 27: Developing microservices with aggregates (devnexus2017)

@crichardson

Transaction scope =

NoSQL database “transaction”

Page 28: Developing microservices with aggregates (devnexus2017)

@crichardson

Agenda

The problem with Domain Models and microservices

Overview of aggregates

Maintaining consistency between aggregates

Using event sourcing with Aggregates

Customers and Orders example

Using explicit saga orchestration

Page 29: Developing microservices with aggregates (devnexus2017)

@crichardson

Customer management

How to maintain data consistency between aggregates?

Order management

Order Service

placeOrder()

Customer Service

updateCreditLimit()

Customer

creditLimit ...

has ordersbelongs toOrder

total

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

?

Page 30: Developing microservices with aggregates (devnexus2017)

@crichardson

Saga

Using event-driven Sagas instead of 2PCDistributed transaction

Order Customer

Local transaction

Order

Local transaction

Customer

Local transaction

Order

Event Event

Page 31: Developing microservices with aggregates (devnexus2017)

@crichardson

Order Service

Saga-based, eventually consistent order processing

Customer Service

Order created

Credit Reserved

Credit Check Failed

Create Order

OR Customer

creditLimit creditReservations ...

Order

state total …

approve()/reject()

Event Handler

Event Handler

reserveCredit()

Page 32: Developing microservices with aggregates (devnexus2017)

@crichardson

Order ServiceOrder

id : 4567 total: 343 state = CREATED

Customer Service

Customer creditLimit : 12000 creditReservations: {}

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

Order id : 4567 total: 343 state = APPROVED

Eventually consistent credit checking

Message Bus

createOrder()

Publishes:Subscribes to:

Subscribes to:

publishes:

OrderCreatedEvent

CreditReservedEvent

OrderCreatedEvent CreditReservedEvent

Page 33: Developing microservices with aggregates (devnexus2017)

@crichardson

Complexity of compensating transactions

ACID transactions can simply rollback

BUT Developer must write application logic to “rollback” eventually consistent transactions

For example:

CreditCheckFailed => cancel Order

Money transfer destination account closed => credit source account

Careful design required!

Page 34: Developing microservices with aggregates (devnexus2017)

@crichardson

How atomically update database and publish an event

Order Service

Order Database

Message Broker

insert Order

publish OrderCreatedEvent

dual write problem

?

Page 35: Developing microservices with aggregates (devnexus2017)

@crichardson

Failure = inconsistent system

Order Service

Order Database

Message Broker

insert Order

publish OrderCreatedEvent

X

Page 36: Developing microservices with aggregates (devnexus2017)

@crichardson

2PC is not an option

Page 37: Developing microservices with aggregates (devnexus2017)

@crichardson

Reliably publish events when state changes

Page 38: Developing microservices with aggregates (devnexus2017)

@crichardson

Agenda

The problem with Domain Models and microservices

Overview of aggregates

Maintaining consistency between aggregates

Using event sourcing with Aggregates

Customers and Orders example

Using explicit saga orchestration

Page 39: Developing microservices with aggregates (devnexus2017)

@crichardson

Event sourcing = event-centric persistence

Application

Database

Event store

update

publish

X

Page 40: Developing microservices with aggregates (devnexus2017)

@crichardson

Event sourcing

For each DDD aggregate:

Identify (state changing) domain events

e.g. use Event Storming

Define Event classes

For example, Order events: OrderCreated, OrderCancelled, OrderApproved, OrderRejected, OrderShipped

Page 41: Developing microservices with aggregates (devnexus2017)

@crichardson

Persists events NOT current state

Order

status ….

101 ACCEPTED

Order tableX

Page 42: Developing microservices with aggregates (devnexus2017)

@crichardson

Event-centric schema

Page 43: Developing microservices with aggregates (devnexus2017)

@crichardson

Persists events NOT current state

Event table

Entity type Event id

Entity id

Event data

Order 902101 …OrderApproved

Order 903101 …OrderShipped

Event type

Order 901101 …OrderCreated

Page 44: Developing microservices with aggregates (devnexus2017)

@crichardson

Replay events to recreate state

Order

state

OrderCreated(…) OrderAccepted(…) OrderShipped(…)

Events

Periodically snapshot to avoid loading all events

Page 45: Developing microservices with aggregates (devnexus2017)

@crichardson

The present is a fold over history

currentState = foldl(applyEvent, initialState, events)

Page 46: Developing microservices with aggregates (devnexus2017)

@crichardson

Event Store

Application architecture

Order 123 Customer 456

OrderCreated OrderApproved …

CustomerCreated CustomerCreditReserved …

CreateOrder UpdateOrder GetOrder

Subscribe

Order Service

CreateCustomer UpdateCustomer GetCustomer

Subscribe

Customer Service

Page 47: Developing microservices with aggregates (devnexus2017)

@crichardson

Request handling in an event sourced application

HTTP Handler

Event Store

pastEvents = findEvents(orderId)

Order

new()

applyEvents(pastEvents)

newEvents = processCmd(someCmd)

saveEvents(newEvents) (optimistic locking)

Order Service

applyEvents(newEvents)

Page 48: Developing microservices with aggregates (devnexus2017)

@crichardson

Event Store publishes events consumed by other services

Event Store

Event Subscriber

subscribe(EventTypes)

publish(event)

publish(event)

Customer

update()

Customer Service

Page 49: Developing microservices with aggregates (devnexus2017)

@crichardson

Event Store publishes events consumed by other services

Event Store

Event Subscriber

subscribe(EventTypes)

publish(event)

publish(event)

CQRS View

update()

Service Xyz

send notifications

Page 50: Developing microservices with aggregates (devnexus2017)

Event store = database + message broker

Home grown/DIY

.NET geteventstore.com

https://www.lightbend.com/lagom

http://eventuate.io (mine)

Event Store

Save aggregate

events

Get aggregate

events

Subscribe to events

Page 51: Developing microservices with aggregates (devnexus2017)

@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 52: Developing microservices with aggregates (devnexus2017)

@crichardson

Drawbacks of event sourcing…

Requires application rewrite

Weird and unfamiliar style of programming

Events = a historical record of your bad design decisions

Must detect and ignore duplicate events

Idempotent event handlers

Track most recent event and ignore older ones

Page 53: Developing microservices with aggregates (devnexus2017)

@crichardson

… Drawbacks of event sourcing

Querying the event store can be challenging

Some queries might be complex/inefficient, e.g. accounts with a balance > X

Event store might only support lookup of events by entity id

Must use Command Query Responsibility Segregation (CQRS) to handle queries ⇒ application must handle eventually consistent data

Page 54: Developing microservices with aggregates (devnexus2017)

@crichardson

Agenda

The problem with Domain Models and microservices

Overview of aggregates

Maintaining consistency between aggregates

Using event sourcing with Aggregates

Customers and Orders example

Using explicit saga orchestration

Page 55: Developing microservices with aggregates (devnexus2017)

@crichardson

Orders and Customers example

Kafka

Rest API

Event Store

Event Store

Adapter

Customer Service

Rest APIOrder Service

Customer Aggregate

Order Aggregate

Rest APIOrder HistoryService

MongoDB

CQRS View

Event Store

Adapter

Event Store

Adapter

Used to notify Order Service of invalid

customer ids!

Page 56: Developing microservices with aggregates (devnexus2017)

@crichardson

Customer microservice

Customer Service

CustomerController

Spring Cloud

StreamKafka

Customer Aggregate

Rest API

Customer Event Handlers

EventStore

Event Store

Adapter

Order*Event

POST /customers

Page 57: Developing microservices with aggregates (devnexus2017)

@crichardson

The Customer aggregate

Money creditLimit Map<OrderId, Money> creditReservations

Customer

List<Event> process(CreateCustomerCommand cmd) { … } List<Event> process(ReserveCreditCommand cmd) { … } … void apply(CustomerCreatedEvent anEvent) { … } void apply(CreditReservedEvent anEvent) { … } …

State

Behavior

Page 58: Developing microservices with aggregates (devnexus2017)

@crichardson

Familiar concepts restructured

class Customer {

public void reserveCredit( orderId : String, amount : Money) {

// verify

// update state this.xyz = … }

public List<Event> process( ReserveCreditCommand cmd) { // verify … return singletonList( new CreditReservedEvent(…); ) }

public void apply( CreditReservedEvent event) { // update state this.xyz = event.xyz }

Page 59: Developing microservices with aggregates (devnexus2017)

@crichardson

Customer command processing

Page 60: Developing microservices with aggregates (devnexus2017)

@crichardson

Customer applying events

Page 61: Developing microservices with aggregates (devnexus2017)

@crichardson

Customer Controller

Page 62: Developing microservices with aggregates (devnexus2017)

@crichardson

Creating a Customer

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

Page 63: Developing microservices with aggregates (devnexus2017)

@crichardson

Event handling in CustomersDurable subscription name

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

Triggers BeanPostProcessor

Publish message to Spring Cloud Stream (Kafka) when Customer not found

Page 64: Developing microservices with aggregates (devnexus2017)

@crichardson

Event handler in Orders

Page 65: Developing microservices with aggregates (devnexus2017)

@crichardson

Agenda

The problem with Domain Models and microservices

Overview of aggregates

Maintaining consistency between aggregates

Using event sourcing with Aggregates

Customers and Orders example

Using explicit saga orchestration

Page 66: Developing microservices with aggregates (devnexus2017)

@crichardson

Order Service

Order Service ⬄ Customer Service: tightly coupled, cyclic dependency 😢

Customer Service

Order created

Credit Reserved

Credit Check FailedOR

Must subscribe to all Order events that

affect available credit!Order …

Page 67: Developing microservices with aggregates (devnexus2017)

@crichardson

Order Service

Decouple by maintaining available credit in OrderService

Customer ServiceAvailable Credit Changed

Customer CreatedCustomer

creditLimit availableCredit ...

Order

state total …

AvailableCredit Tracker

creditLimit availableCredit ...

Simpler 😄 Still cyclic 😢

Page 68: Developing microservices with aggregates (devnexus2017)

Use an explicit sagaSaga object that orchestrates the flow

Created in response to an initial event, e.g. OrderCreated

A state machine

Receives events from aggregates

Sends commands to aggregates

Implemented as an event-sourced aggregate

Saga

Event

Command

Page 69: Developing microservices with aggregates (devnexus2017)

@crichardson

Customer Service

Order Service

Using an OrderCreationSaga

Order

state total …

AvailableCredit Tracker

creditLimit availableCredit ...

Order Creation

Saga

create()create()

Reserve Credit

Credit Reserved

Approve Order

Customer

Update Available

Credit

OrderCreated

Acyclic 😄

Page 70: Developing microservices with aggregates (devnexus2017)

@crichardson

Order saga state machine

NEW

SUCCEEDED

FAILED

OrderCreatedEvent/ reserveCredit()

CustomerCreditReservedEvent /approveOrder()

EntityNotFoundCommandExecutionErrorCustomerCreditLimitExceededEvent /

rejectOrder()

Page 71: Developing microservices with aggregates (devnexus2017)

@crichardson

Saga as a singleton state machine

A state machine state e.g. enum { NEW, …}

A data object

Page 72: Developing microservices with aggregates (devnexus2017)

@crichardson

Event processing

(State + Data, Event) ⇒

(State’+ Data’, Commands)

Page 73: Developing microservices with aggregates (devnexus2017)

@crichardson

(OrderSagaState, OrderSagaData, Event) ⇒

(Commands, OrderSagaState’, OrderSagaData’)

Page 74: Developing microservices with aggregates (devnexus2017)

@crichardson

OrderCreationSaga state machine definition

event ⇒ saga id

starting + state transitions

Page 75: Developing microservices with aggregates (devnexus2017)

@crichardson

Initial actionTriggers saga

creation

1. Send command 2. Transition to new state 3. Create data

Page 76: Developing microservices with aggregates (devnexus2017)

@crichardson

Event handlerTriggering

event

1. Send command 2. Transition to new state

Page 77: Developing microservices with aggregates (devnexus2017)

@crichardson

Kafka-based command bus

Order Saga

Credit Tracking

Aggregate

CreditTrackingCommand Topic

OrderSagaCommandFailure Topic

Reserve Credit

CustomerNotFound

Per aggregate

type

Per saga type

Page 78: Developing microservices with aggregates (devnexus2017)

@crichardson

Summary

Aggregates are the building blocks of microservices

Use event-driven sagas to maintain consistency between aggregates

Event sourcing is a good way to implement a event-driven architecture

Use explicit sagas to orchestrate transactions

Page 79: Developing microservices with aggregates (devnexus2017)

@crichardson

@crichardson [email protected]

http://learnmicroservices.io

Questions?