building microservices with scala, functional domain models and spring boot (#devnexus)

77
@crichardson Building microservices with Scala, functional domain models and Spring Boot Chris Richardson Author of POJOs in Action Founder of the original CloudFoundry.com @crichardson [email protected] http://plainoldobjects.com http://microservices.io

Upload: chris-richardson

Post on 15-Jul-2015

1.856 views

Category:

Software


3 download

TRANSCRIPT

Page 1: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Building microservices with Scala, functional domain models and Spring Boot

Chris Richardson

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

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

Page 2: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Presentation goal

Share my experiences with building and deploying an application using Scala, functional domain models, microservices, event sourcing,

CQRS, and Docker

Page 3: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

About Chris

Page 4: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

About Chris

Founder of a buzzword compliant (stealthy, social, mobile, big data, machine learning, ...) startup

Consultant helping organizations improve how they architect and deploy applications using cloud, micro services, polyglot applications, NoSQL, ...

Creator of http://microservices.io

Page 5: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

For more information

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

http://microservices.io

http://plainoldobjects.com/

https://twitter.com/crichardson

Page 6: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Agenda

Why build event-driven microservices?

Overview of event sourcing

Designing microservices with event sourcing

Implementing queries in an event sourced application

Building and deploying microservices

Page 7: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Tomcat

Traditional application architecture

Browser/Client

WAR/EAR

RDBMS

Customers

Accounts

Transfers

Banking

develop test

deploy

Simple

Load balancer

scale

Spring MVC

Spring Hibernate

...

HTML

REST/JSON

ACID

Page 8: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Limitations of the monolithic architecture

Intimidates developers

Obstacle to frequent deployments

Overloads your IDE and container

Obstacle to scaling development

Modules having conflicting scaling requirements

Requires long-term commitment to a technology stack

Page 9: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Limitations of a single relational database

Scalability

Distribution

Schema updates

O/R impedance mismatch

Handling semi-structured data

Page 10: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Apply the scale cube

X axis - horizontal duplication

Z axis

- data

partit

ioning

Y axis - functional

decomposition

Scale b

y split

ting s

imilar

thing

s

Scale by splitting

different things

Page 11: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Use a microservice architecture

Banking UI

Account Management Service

MoneyTransfer Management Service

Account Database

MoneyTransfer Database

Standalone services

Page 12: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Use functionally decomposed

and sharded relational databases

Page 13: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Use NoSQL databasesAvoids the limitations of RDBMS

For example,

text search ⇒ Solr/Cloud Search

social (graph) data ⇒ Neo4J

highly distributed/available database ⇒ Cassandra

But most have a very limited transaction model

Page 14: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Different modules use different types of databases

IEEE Software Sept/October 2010 - Debasish Ghosh / Twitter @debasishg

Page 15: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

But this results in distributed data management problems

Page 16: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Example #1 - SQL + Text Search engine

Application

MySQL ElasticSearch

How to maintain consistency without 2PC?

Product #1 Product #1

Page 17: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Example #2 - Cassandra main table <=> index table

Application

Cassandra

How to maintain consistency without 2PC?

Main Table

Denormalized view

Index Table

Page 18: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Example #3: Money transferAccount Management

ServiceMoneyTransfer

Management Service

Account Database

MoneyTransfer Database

Account #2

Account #1 Money Transfer

How to maintain consistency without 2PC?

Page 19: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Event-based architecture to the rescue

Components (e.g. services) publish events when state changes

Components subscribe to events

Maintains eventual consistency across multiple aggregates (in multiple datastores)

Synchronize replicated data

Page 20: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Event-driven synchronization: SQL + Text Search engine

Catalog Service

MySQL ElasticSearch

Product #1 Product #1

Search Service

Message Bus

Insert Product Created

Product Created Index Doc

create product

Page 21: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

MoneyTransferServiceMoneyTransfer

fromAccountId = 101 toAccountId = 202 amount = 55 state = INITIAL

MoneyTransfer fromAccountId = 101 toAccountId = 202 amount = 55 state = DEBITED

MoneyTransfer fromAccountId = 101 toAccountId = 202 amount = 55 state = COMPLETED

Eventually consistent money transfer

Message Bus

AccountService

transferMoney()

Publishes:Subscribes to:

Subscribes to:

publishes:

MoneyTransferCreatedEvent

AccountDebitedEvent

DebitRecordedEvent

AccountCreditedEventMoneyTransferCreatedEvent

DebitRecordedEvent

AccountDebitedEventAccountCreditedEvent

Account id = 101 balance = 250

Account id = 202 balance = 125

Account id = 101 balance = 195

Account id = 202 balance = 180

Page 22: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

To maintain consistency a service must

atomically publish an event whenever

a domain object changes

Page 23: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

How to atomically update the datastore and publish event(s)?

Use 2PC

Guaranteed atomicity BUT

Need a distributed transaction manager

Database and message broker must support 2PC

Impacts reliability

Not fashionable

2PC is best avoided

Use datastore as a message queue

1. Update database: new entity state & event

2. Consume event & mark event as consumed

Eventually consistent mechanism

See BASE: An Acid Alternative, http://bit.ly/ebaybase

• BUT Tangled business logic and event publishing code

• Difficult to implement when using a NoSQL database :-(

Page 24: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Agenda

Why build event-driven microservices?

Overview of event sourcing

Designing microservices with event sourcing

Implementing queries in an event sourced application

Building and deploying microservices

Page 25: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Event sourcingFor each aggregate in your domain model:

Identify (state-changing) domain events

Define Event classes

For example,

Account: AccountOpenedEvent, AccountDebitedEvent, AccountCreditedEvent

ShoppingCart: ItemAddedEvent, ItemRemovedEvent, OrderPlacedEvent

Page 26: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@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 27: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Replay events to recreate state

Account

balance

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

Events

Page 28: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Before: update state + publish events

Two actions that must be atomic

Single action that can be done atomically

Now: persist (and publish) events

Page 29: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@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

Page 30: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Event Store publishes events - consumed by other services

Event Store

Event Subscriber

subscribe(EventTypes)

publish(event)

publish(event)

Aggregate

NoSQL materialized

view

update()

update()

Microservice B

Page 31: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Event store implementations

Home-grown/DIY

geteventstore.com by Greg Young

Talk to me about my project :-)

Page 32: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Optimizing using snapshots

Most aggregates have relatively few events

BUT consider a 10-year old Account ⇒ many transactions

Therefore, use snapshots:

Periodically save snapshot of aggregate state

Typically serialize a memento of the aggregate

Load latest snapshot + subsequent events

Page 33: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Hybrid OO/Functional style example aggregate

Page 34: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Aggregate traits

Map Command to Events

Apply event returning updated Aggregate

Page 35: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Account - command processing

Prevent overdraft

Page 36: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Account - applying eventsImmutable

Page 37: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Event Store API

Reactive/Async API

Page 38: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Functional example aggregate

Page 39: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Aggregate type classes/implicits

Page 40: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Functional-style MoneyTransfer Aggregate

State Behavior

Page 41: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

FP-style event storeEnables inference of T, and EV

Tells ES how to instantiate aggregate and apply events

Page 42: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Business benefits of event sourcing

Built-in, reliable audit log

Enables temporal queries

Publishes events needed by big data/predictive analytics etc.

Preserved history ⇒ More easily implement future requirements

Page 43: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Technical benefits of event sourcing

Solves data consistency issues in a Microservice/NoSQL-based architecture:

Atomically save and publish events

Event subscribers update other aggregates ensuring eventual consistency

Event subscribers update materialized views in SQL and NoSQL databases (more on that later)

Eliminates O/R mapping problem

Page 44: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@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 (more on that later)

Page 45: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Agenda

Why build event-driven microservices?

Overview of event sourcing

Designing microservices with event sourcing

Implementing queries in an event sourced application

Building and deploying microservices

Page 46: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Identify your application’s bounded contexts =

Subdomains / areas of functionality =>

microservice

Page 47: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Partition the bounded context’s

domain model into Aggregates

Page 48: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

The anatomy of a microservice

Event Store

HTTP Request

HTTP Adapter

Event Adapter

Cmd

Cmd

EventsEvents

Xyz Adapter

Xyz Request

microservice

Aggregate

Page 49: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Asynchronous Spring MVC controller

Scala Future => Spring MVC DeferredResult

Page 50: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

MoneyTransferService

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

Page 51: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Handling events published by Accounts

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

Page 52: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Agenda

Why build event-driven microservices?

Overview of event sourcing

Designing microservices with event sourcing

Implementing queries in an event sourced application

Building and deploying microservices

Page 53: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Let’s imagine that you want to display an account and it’s recent transactions...

Page 54: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Displaying balance + recent credits and debits

We need to do a “join: between the Account and the corresponding MoneyTransfers

(Assuming Debit/Credit events don’t include other account, ...)

BUT Event Store = primary key lookup of individual aggregates, ...

⇒ Use Command Query Responsibility Segregation

Page 55: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Command Query Responsibility Segregation (CQRS)

Command-side

Commands

Aggregate

Event Store

Events

Query-side

Queries

(Denormalized) View

Events

Page 56: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Query-side microservices

Event Store

Updater - microservice

View Updater Service

EventsReader - microservice

HTTP GET Request

View Query Service

View Store

e.g. MongoDB

Neo4J CloudSearch

update query

Page 57: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Persisting account balance and recent transactions in MongoDB

{ id: "298993498", balance: 100000, transfers : [

{"transferId" : "4552840948484", "fromAccountId" : 298993498, "toAccountId" : 3483948934, "amount" : 5000}, ...

], changes: [ {"changeId" : "93843948934", "transferId" : "4552840948484", "transactionType" : "AccountDebited", "amount" : 5000}, ... ] }

Denormalized = efficient lookup

MoneyTransfers that update the account

The debits and credits

Current balance

Page 58: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Persisting account info using MongoDB...

class AccountInfoUpdateService (accountInfoRepository : AccountInfoRepository, mongoTemplate : MongoTemplate) extends CompoundEventHandler {

@EventHandlerMethod def created(de: DispatchedEvent[AccountOpenedEvent]) = …

@EventHandlerMethod def recordDebit(de: DispatchedEvent[AccountDebitedEvent]) = …

@EventHandlerMethod def recordCredit(de: DispatchedEvent[AccountCreditedEvent]) = …

@EventHandlerMethod def recordTransfer(de: DispatchedEvent[MoneyTransferCreatedEvent]) = …

}

Page 59: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

Other kinds of viewsAWS Cloud Search

Text search as-a-Service

View updater batches aggregates to index

View query service does text search

AWS DynamoDB

NoSQL as-a-Service

On-demand scalable - specify desired read/write capacity

Document and key-value data models

Useful for denormalized, UI oriented views

Page 60: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

Benefits and drawbacks of CQRS

Benefits

Necessary in an event-sourced architecture

Separation of concerns = simpler command and query models

Supports multiple denormalized views

Improved scalability and performance

Drawbacks

Complexity

Potential code duplication

Replication lag/eventually consistent views

Page 61: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Agenda

Why build event-driven microservices?

Overview of event sourcing

Designing microservices with event sourcing

Implementing queries in an event sourced application

Building and deploying microservices

Page 62: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

My application architecture

API gateway Event

Store

Service 1

Service 2

Service ...

Event Archiver

IndexerAWS Cloud Search

S3

NodeJS Scala/Spring Boot

Page 63: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Building microservices with Spring Boot

Makes it easy to create stand-alone, production ready Spring applications

Automatically configures Spring using Convention over Configuration

Externalizes configuration

Generates standalone executable JARs with embedded web server

Provides a standard foundation for all your microservices

Page 64: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Spring Boot simplifies configuration

Spring Container

Application components

Fully configured application

Configuration Metadata

•Typesafe JavaConfig •Annotations •Legacy XML

Default Configuration

Metadata

Spring BootYou write less

of this

Inferred from CLASSPATH

Page 65: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Tiny Spring configurationScan for controllers

Customize JSON serialization

Page 66: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

About auto-configurationBuilds on Spring framework features

@EnableAutoConfiguration - triggers the inclusion of default configuration

@Conditional - beans only active if condition is satisfied

Conditional on class defined on class path

e.g. Mongo Driver implies Mongo beans

Conditional on bean defined/undefined

e.g. define Mongo beans if you haven’t

Page 67: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

The Main program

Page 68: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Building with Gradle

buildscript { repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:1.1.8.RELEASE") } }

apply plugin: 'scala' apply plugin: 'spring-boot'

dependencies { compile "org.springframework.boot:spring-boot-starter-web" compile "org.springframework.boot:spring-boot-starter-data-mongodb" compile "org.springframework.boot:spring-boot-starter-amqp" compile "org.springframework.boot:spring-boot-starter-actuator"

testCompile "org.springframework.boot:spring-boot-starter-test" } ...

Ensures correct dependencies

Page 69: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Running the microservice

$ java -jar build/libs/spring-boot-restful-service.jar --server.port=8081 ... 2014-12-03 16:32:04.671 INFO 93199 --- [ main] n.c.m.r.main.UserRegistrationMain$ : Started UserRegistrationMain. in 5.707 seconds (JVM running for 6.553)

$ curl localhost:8081/health {"status":"UP", "mongo":{"status":"UP","version":"2.4.10"}, "rabbit":{"status":"UP", ...} }

Built in health checks

Command line arg processing

Page 70: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Jenkins-based deployment pipeline

Build & Test microservice

Build & Test Docker image

Deploy Docker image

to registry

One pipeline per microservice

Page 71: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Building Docker images

cp ../build/libs/service.${1}.jar build/service.jar

docker build -t service-${VERSION} .

docker/build.sh

Building only takes 5 seconds!

Page 72: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Smoke testing docker images

Smoke test

Docker daemon

Service containerGET /health

POST /containers/create

creates

POST /containers/{id}/start

Docker daemon must listen on TCP port

Page 73: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Publishing Docker images

docker tag service-${VERSION}:latest \ ${REGISTRY_HOST_AND_PORT}/service-${VERSION}

docker push ${REGISTRY_HOST_AND_PORT}/service-${VERSION}

docker/publish.sh

Pushing only takes 25 seconds!

Page 74: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

CI environment runs on Docker

EC2 Instance

Jenkins Container

Artifactory container

EBS volume

/jenkins-home

/gradle-home

/artifactory-home

Page 75: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Updating the production environment

Large EC2 instance running Docker

Deployment tool:

1. Compares running containers with what’s been built by Jenkins

2. Pulls latest images from Docker registry

3. Stops old versions

4. Launches new versions

One day: use Docker clustering solution and a service discovery mechanism,

Most likely, AWS container service

Mesos and Marathon + Zookeeper, Kubernetes or ???

Page 76: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

Summary

Event sourcing solves key data consistency issues with:

Microservices

Partitioned SQL/NoSQL databases

Use CQRS to implement materialized views for queries

Spring Boot is a great foundation for microservices

Docker is a great way to package microservices

Page 77: Building microservices with Scala, functional domain models and Spring Boot (#Devnexus)

@crichardson

@crichardson [email protected]

http://plainoldobjects.com http://microservices.io