68
@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

Developing microservices with aggregates (SpringOne platform, #s1p)

Embed Size (px)

Citation preview

Page 1: Developing microservices with aggregates (SpringOne platform, #s1p)

@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 (SpringOne platform, #s1p)

@crichardson

Presentation goal

Show how Domain Driven Design Aggregates

and Microservices are a perfect match

Page 3: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

About Chris

Page 4: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

About Chris

Consultant and trainer focusing on modern

application architectures including microservices

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

Page 5: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

About Chris

Founder of a startup that is creating a platform that makes it easier for developers to write transactional

microservices (http://eventuate.io)

Page 6: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

For more information

http://learnmicroservices.io

Page 7: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Agenda

The problem with Domain Models and microservices

Overview of aggregates

Maintaining consistency between aggregates

Using event sourcing with Aggregates

Example application

Page 8: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

The Microservice architecture tackles complexity through

modularization

Page 9: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Microservice =

Business capability

Page 10: Developing microservices with aggregates (SpringOne platform, #s1p)

@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

Apply X-axis and Z-axis scaling to each service

independently

Page 11: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Service boundaries enforce modularity

Page 12: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

But there are challenges…

Page 13: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Product service

Customer serviceOrder service

Domain model = tangled web of classes

Order

OrderLine Item

quantity

Addressstreet city …

Customer

Productname price

?

?

creditLimit

Page 14: Developing microservices with aggregates (SpringOne platform, #s1p)

@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 15: Developing microservices with aggregates (SpringOne platform, #s1p)

@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 16: Developing microservices with aggregates (SpringOne platform, #s1p)

@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 17: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

2PC is not a viable option

Guarantees consistency

BUT

2PC is best avoided

Not supported by many NoSQL databases etc.

CAP theorem ⇒ 2PC impacts availability

….

Page 18: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

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

Page 19: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Agenda

The problem with Domain Models and microservices

Overview of aggregates

Maintaining consistency between aggregates

Using event sourcing with Aggregates

Example application

Page 20: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Domain Driven Design: read it!

Page 21: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Domain Driven Design - building blocks

Entity

Value object

Services

Repositories

Aggregates

Adopted

Ignored (at least by me)

Page 22: Developing microservices with aggregates (SpringOne platform, #s1p)

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 23: Developing microservices with aggregates (SpringOne platform, #s1p)

Aggregate: rule #1

Reference other aggregate roots via

identity

(primary key)

Order

OrderLine Item

quantity productId productName productPrice

customerId

Address

street city …

Page 24: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Foreign keys in a Domain Model?!?

Page 25: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Domain model = collection of loosely connected aggregates

Order

OrderLine Item

quantity

Addressstreet city …

Customer

Productname price

Page 26: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Product service

Customer serviceOrder service

Easily partition into microservices

Order

OrderLine Item

quantity

Addressstreet city …

Customer

Productname price

Page 27: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Aggregate rule #2

Transaction =

processing one command by one aggregate

Page 28: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Product service

Customer serviceOrder service

Transaction scope = service

Order

OrderLine Item

quantity

Addressstreet city …

Customer

Productname price

Page 29: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Transaction scope =

NoSQL database “transaction”

Page 30: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Aggregate granularity

If an update must be atomic then it must be handled by a single aggregate

Therefore

Aggregate granularity is important

Page 31: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Aggregate granularity

Consistency Scalability/ User experience

Customer

Order

Product

Customer

Order

Product

Customer

Order

Product

Page 32: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Agenda

The problem with Domain Models and microservices

Overview of aggregates

Maintaining consistency between aggregates

Using event sourcing with Aggregates

Example application

Page 33: Developing microservices with aggregates (SpringOne platform, #s1p)

@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 34: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Event-driven architecture

Page 35: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Use event-driven, eventually consistent order processing

Order Service

Customer Service

Order created

Credit Reserved

Credit Check Failed

Create Order

OR

Customer

creditLimit creditReservations ...

Order

state total …

create()reserveCredit()

approve()/reject()

Page 36: Developing microservices with aggregates (SpringOne platform, #s1p)

@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 = APPROVED

Eventually consistent credit checking

Message Bus

createOrder()

Publishes:Subscribes to:

Subscribes to:

publishes:

OrderCreatedEvent

CreditReservedEvent

OrderCreatedEvent CreditReservedEvent

Page 37: Developing microservices with aggregates (SpringOne platform, #s1p)

@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 38: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

How atomically update database and publish an event

Order Service

Order Database

Message Broker

insert Order

publish OrderCreatedEvent

dual write problem

?

Page 39: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Failure = inconsistent system

Order Service

Order Database

Message Broker

insert Order

publish OrderCreatedEvent

X

Page 40: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

2PC is not an option

Page 41: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Reliably publish events when state changes

Page 42: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Agenda

The problem with Domain Models and microservices

Overview of aggregates

Maintaining consistency between aggregates

Using event sourcing with Aggregates

Example application

Page 43: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Event sourcing = event-centric persistence

Application

Database

Event store

update

publish

X

Page 44: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Event sourcing

For each domain object (i.e. 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 45: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Persists events NOT current state

Order

status ….

101 ACCEPTED

Order tableX

Page 46: Developing microservices with aggregates (SpringOne platform, #s1p)

@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 47: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Replay events to recreate state

Order

state

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

Events

Periodically snapshot to avoid loading all events

Page 48: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

The present is a fold over history

currentState = foldl(applyEvent, initialState, events)

Page 49: Developing microservices with aggregates (SpringOne platform, #s1p)

@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 50: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Request handling in an event sourced application

HTTP Handler

Event Store

pastEvents = findEvents(entityId)

Order

new()

applyEvents(pastEvents)

newEvents = processCmd(someCmd)

saveEvents(newEvents) (optimistic locking)

Order Service

applyEvents(newEvents)

Page 51: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Event Store publishes events consumed by other services

Event Store

Event Subscriber

subscribe(EventTypes)

publish(event)

publish(event)

Customer

update()

Customer Service

Page 52: Developing microservices with aggregates (SpringOne platform, #s1p)

@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 53: Developing microservices with aggregates (SpringOne platform, #s1p)

Event store = database + message broker

Hybrid database and message broker

Implementations:

Home grown/DIY

geteventstore.com by Greg Young

http://eventuate.io (mine)

Event Store

Save aggregate

events

Get aggregate

events

Subscribe to events

Page 54: Developing microservices with aggregates (SpringOne platform, #s1p)

@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 55: Developing microservices with aggregates (SpringOne platform, #s1p)

@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 56: Developing microservices with aggregates (SpringOne platform, #s1p)

@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 57: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Agenda

The problem with Domain Models and microservices

Overview of aggregates

Maintaining consistency between aggregates

Using event sourcing with Aggregates

Example application

Page 58: Developing microservices with aggregates (SpringOne platform, #s1p)

@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

Page 59: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Customer microservice

Customer Service

CustomerController

Spring Cloud

StreamKafka

Customer Aggregate

Rest API

Customer Event Handlers

EventStore

Event Store

Adapter

Page 60: Developing microservices with aggregates (SpringOne platform, #s1p)

@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 61: Developing microservices with aggregates (SpringOne platform, #s1p)

@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 62: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Customer command processing

Page 63: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Customer applying events

Page 64: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Customer Controller

Page 65: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Creating a Customer

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

Page 66: Developing microservices with aggregates (SpringOne platform, #s1p)

@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 67: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

Summary

Aggregates are the building blocks of microservices

Use events to maintain consistency between aggregates

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

Page 68: Developing microservices with aggregates (SpringOne platform, #s1p)

@crichardson

@crichardson [email protected]

http://learnmicroservices.io

Questions?