Developing microservices with aggregates (SpringOne platform, #s1p)

Post on 10-Jan-2017

4.809 views 5 download

Transcript of 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 chris@chrisrichardson.net http://eventuate.io

@crichardson

Presentation goal

Show how Domain Driven Design Aggregates

and Microservices are a perfect match

@crichardson

About Chris

@crichardson

About Chris

Consultant and trainer focusing on modern

application architectures including microservices

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

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

@crichardson

For more information

http://learnmicroservices.io

@crichardson

Agenda

The problem with Domain Models and microservices

Overview of aggregates

Maintaining consistency between aggregates

Using event sourcing with Aggregates

Example application

@crichardson

The Microservice architecture tackles complexity through

modularization

@crichardson

Microservice =

Business capability

@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

@crichardson

Service boundaries enforce modularity

@crichardson

But there are challenges…

@crichardson

Product service

Customer serviceOrder service

Domain model = tangled web of classes

Order

OrderLine Item

quantity

Addressstreet city …

Customer

Productname price

?

?

creditLimit

@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

@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

@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

@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

….

@crichardson

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

@crichardson

Agenda

The problem with Domain Models and microservices

Overview of aggregates

Maintaining consistency between aggregates

Using event sourcing with Aggregates

Example application

@crichardson

Domain Driven Design: read it!

@crichardson

Domain Driven Design - building blocks

Entity

Value object

Services

Repositories

Aggregates

Adopted

Ignored (at least by me)

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 …

Aggregate: rule #1

Reference other aggregate roots via

identity

(primary key)

Order

OrderLine Item

quantity productId productName productPrice

customerId

Address

street city …

@crichardson

Foreign keys in a Domain Model?!?

@crichardson

Domain model = collection of loosely connected aggregates

Order

OrderLine Item

quantity

Addressstreet city …

Customer

Productname price

@crichardson

Product service

Customer serviceOrder service

Easily partition into microservices

Order

OrderLine Item

quantity

Addressstreet city …

Customer

Productname price

@crichardson

Aggregate rule #2

Transaction =

processing one command by one aggregate

@crichardson

Product service

Customer serviceOrder service

Transaction scope = service

Order

OrderLine Item

quantity

Addressstreet city …

Customer

Productname price

@crichardson

Transaction scope =

NoSQL database “transaction”

@crichardson

Aggregate granularity

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

Therefore

Aggregate granularity is important

@crichardson

Aggregate granularity

Consistency Scalability/ User experience

Customer

Order

Product

Customer

Order

Product

Customer

Order

Product

@crichardson

Agenda

The problem with Domain Models and microservices

Overview of aggregates

Maintaining consistency between aggregates

Using event sourcing with Aggregates

Example application

@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

?

@crichardson

Event-driven architecture

@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()

@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

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

@crichardson

How atomically update database and publish an event

Order Service

Order Database

Message Broker

insert Order

publish OrderCreatedEvent

dual write problem

?

@crichardson

Failure = inconsistent system

Order Service

Order Database

Message Broker

insert Order

publish OrderCreatedEvent

X

@crichardson

2PC is not an option

@crichardson

Reliably publish events when state changes

@crichardson

Agenda

The problem with Domain Models and microservices

Overview of aggregates

Maintaining consistency between aggregates

Using event sourcing with Aggregates

Example application

@crichardson

Event sourcing = event-centric persistence

Application

Database

Event store

update

publish

X

@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

@crichardson

Persists events NOT current state

Order

status ….

101 ACCEPTED

Order tableX

@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

@crichardson

Replay events to recreate state

Order

state

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

Events

Periodically snapshot to avoid loading all events

@crichardson

The present is a fold over history

currentState = foldl(applyEvent, initialState, events)

@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

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

@crichardson

Event Store publishes events consumed by other services

Event Store

Event Subscriber

subscribe(EventTypes)

publish(event)

publish(event)

Customer

update()

Customer Service

@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

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

@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

@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

@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

@crichardson

Agenda

The problem with Domain Models and microservices

Overview of aggregates

Maintaining consistency between aggregates

Using event sourcing with Aggregates

Example application

@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

@crichardson

Customer microservice

Customer Service

CustomerController

Spring Cloud

StreamKafka

Customer Aggregate

Rest API

Customer Event Handlers

EventStore

Event Store

Adapter

@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

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

@crichardson

Customer command processing

@crichardson

Customer applying events

@crichardson

Customer Controller

@crichardson

Creating a Customer

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

@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

@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

@crichardson

@crichardson chris@chrisrichardson.net

http://learnmicroservices.io

Questions?