Developing event-driven microservices with event sourcing and CQRS (phillyete)
-
Upload
chris-richardson -
Category
Software
-
view
907 -
download
1
Transcript of Developing event-driven microservices with event sourcing and CQRS (phillyete)
@crichardson
Developing event-driven microservices with event
sourcing and CQRSChris Richardson
Author of POJOs in Action Founder of the original CloudFoundry.com
@crichardson [email protected] http://plainoldobjects.com http://microservices.io
@crichardson
Presentation goal
Show how Event Sourcing and Command Query Responsibility Segregation
(CQRS) are a great way to implement microservices
@crichardson
About Chris
@crichardson
About Chris
Founder of a startup that’s creating a platform for developing event-driven microservices
Consultant helping organizations improve how they architect and deploy applications using cloud, micro services, polyglot applications, NoSQL, ...
Creator of http://microservices.io
@crichardson
For more information
https://github.com/cer/event-sourcing-examples
http://microservices.io
http://plainoldobjects.com/
https://twitter.com/crichardson
@crichardson
Agenda
Why build event-driven microservices?
Overview of event sourcing
Designing microservices with event sourcing
Implementing queries in an event sourced application
@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
@crichardson
But large monolithic applications
= Trouble!
@crichardson
Intimidates developers
@crichardson
Obstacle to frequent deployments
Need to redeploy everything to change one component
Interrupts long running background (e.g. Quartz) jobs
Increases risk of failure
Fear of change
Updates will happen less often - really long QA cycles
e.g. Makes A/B testing UI really difficult
Eggs in one basket
@crichardson
Overloads your IDE and container
Slows down development
@crichardson
Lots of coordination and communication required
Obstacle to scaling development
I want to update the UI
But the backend is not working
yet!
@crichardson
Requires long-term commitment to a technology stack
@crichardson
Limitations of a single relational database
Scalability
Distribution
Schema updates
O/R impedance mismatch
Handling semi-structured data
@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
@crichardson
Use a microservice architecture
Banking UI
Account Management Service
MoneyTransfer Management Service
Account Database
MoneyTransfer Database
Standalone services
@crichardson
Use functionally decomposed
and sharded relational databases
@crichardson
Use NoSQL databases
Avoids the limitations of RDBMS
Text search ⇒ Solr/Cloud Search
Social (graph) data ⇒ Neo4J
Highly distributed/available database ⇒ Cassandra
…
@crichardson
Different modules use different types of databases
IEEE Software Sept/October 2010 - Debasish Ghosh / Twitter @debasishg
@crichardson
But this results in distributed data management problems
@crichardson
Example #1 - SQL + Text Search engine
Application
MySQL ElasticSearch
How to maintain consistency without 2PC?
Product #1 Product #1
@crichardson
Example #2 - Update two entities in a NoSQL database
Application
MongoDB
How to maintain consistency without transactions?
CollectionDocument 1
Document 2
@crichardson
Example #3 - Cassandra main table <=> index table
Application
Cassandra
How to maintain consistency without 2PC?
Main Table
Denormalized view
Index Table
@crichardson
Example #4: Money transferAccount Management
ServiceMoneyTransfer
Management Service
Account Database A
MoneyTransfer Database
Account #1 Money Transfer
How to maintain consistency without 2PC?
Account Database B
Account #2
@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
@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
@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
@crichardson
To maintain consistency a service must
atomically publish an event whenever
a domain object changes
@crichardson
How to atomically update the datastore and publish event(s)?
@crichardson
Update and publish using 2PC
Guaranteed atomicity BUT
Need a distributed transaction manager
Database and message broker must support 2PC
Impacts reliability
Not fashionable
2PC is best avoided
@crichardson
Use data store as message queue
Use datastore as a message queue
Txn #1: Update database: new entity state & event
Txn #2: Consume event
Txn #3: Mark event as consumed
Eventually consistent mechanism (used by eBay)
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 :-(
@crichardson
Agenda
Why build event-driven microservices?
Overview of event sourcing
Designing microservices with event sourcing
Implementing queries in an event sourced application
@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
@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
@crichardson
Replay events to recreate state
Account
balance
AccountOpenedEvent(balance) AccountDebitedEvent(amount) AccountCreditedEvent(amount)
Events
@crichardson
Before: update state + publish events
Two actions that must be atomic
Single action that can be done atomically
Now: persist (and publish) events
@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
@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
@crichardson
Event store implementations
Home-grown/DIY
geteventstore.com by Greg Young
Talk to me about my project :-)
@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
@crichardson
Hybrid OO/Functional style example aggregate
@crichardson
Aggregate traits
Map Command to Events
Apply event returning updated Aggregate
@crichardson
Account - command processing
Prevent overdraft
@crichardson
Account - applying eventsImmutable
@crichardson
Event Store API
Reactive/Async API
@crichardson
Functional example aggregate
@crichardson
Aggregate type classes/implicits
@crichardson
Functional-style MoneyTransfer Aggregate
State Behavior
@crichardson
FP-style event storeEnables inference of T, and EV
Tells ES how to instantiate aggregate and apply events
@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
@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
@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)
@crichardson
Agenda
Why build event-driven microservices?
Overview of event sourcing
Designing microservices with event sourcing
Implementing queries in an event sourced application
@crichardson
Strategic vs Tactical design
Identify Services
Design Services
@crichardson
Strategic design and
Greenfield development
@crichardson
Strategic design: identify sub-domains
BANKING DOMAIN
@crichardson
Strategic design: identify sub-domains
CUSTOMERMANAGEMENT
ACCOUNT MANAGEMENT
TRANSACTION MANAGEMENT
@crichardson
Strategic design: identify bounded contexts
CUSTOMERMANAGEMENT
ACCOUNT MANAGEMENT
TRANSACTION MANAGEMENT
@crichardson
Strategic design: define microservices
Customer Management
Service
Transaction Management
Service
Account Management
Service
@crichardson
Decide inter-service communication mechanisms
Service X reads from service Y
X makes an RPC call to Y
X consumes events published by Y
Service X updates service Y
X makes an RPC call to Y <= unreliable without 2PC
Y consumes events published by X
@crichardson
Strategic design and
refactoring a monolith
@crichardson
Existing monolith: implement new functionality as a service
Monolith ServiceAnti-
corruption layer
Glue code
Pristine
@crichardson
Existing monolith: extract functionality as a service
Monolith
ServiceIdentify bounded
context
@crichardson
Existing monolith: extract functionality as a service
Monolith ServiceAnti-
corruption layer
Glue code
Pristine
@crichardson
Tactical design
@crichardson
Use the familiar building blocks of DDD
Entity
Value object
Services
Repositories
@crichardson
Partition a bounded context’s
domain model into Aggregates
@crichardson
Identify the state changing events for each Aggregate
@crichardson
Designing domain eventsNaming
Past tense to reflect that something occurred
Ideally specific: AccountOpened/Debited/Credited
Sometimes vague: FooUpdated
Event attributes
Id - TimeUUID
Other attributes - from command, required to persist entity
Event enrichment
ProductAddedToCart(productId) vs. ProductAddedCart(productInfo)
Extra data to support event consumers
@crichardson
The anatomy of a microservice
Event Store
HTTP Request
HTTP Adapter
Event Adapter
Cmd
Cmd
EventsEvents
Xyz Adapter
Xyz Request
microservice
Aggregate
@crichardson
Asynchronous Spring MVC controller
Scala Future => Spring MVC DeferredResult
@crichardson
MoneyTransferService
DSL concisely specifies: 1.Creates MoneyTransfer aggregate 2.Processes command 3.Applies events 4.Persists events
@crichardson
Handling events published by Accounts
1.Load MoneyTransfer aggregate 2.Processes command 3.Applies events 4.Persists events
@crichardson
Agenda
Why build event-driven microservices?
Overview of event sourcing
Designing microservices with event sourcing
Implementing queries in an event sourced application
@crichardson
Let’s imagine that you want to display an account and it’s recent transactions...
@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
@crichardson
Command Query Responsibility Segregation (CQRS)
Command-side
Commands
Aggregate
Event Store
Events
Query-side
Queries
(Denormalized) View
Events
@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
@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
@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]) = …
}
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
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
@crichardson
Summary
Event sourcing solves key data consistency issues with:
Microservices
Partitioned SQL/NoSQL databases
Apply strategic DDD to identify microservices
Apply tactical DDD to design individual services
Use CQRS to implement materialized views for queries