cqrs and event sourcing with mongodb and php

103
CQRS and Event Sourcing with MongoDB and PHP

Upload: davide-bellettini

Post on 28-Jul-2015

285 views

Category:

Software


0 download

TRANSCRIPT

Page 1: CQRS and Event Sourcing with MongoDB and PHP

CQRS and Event Sourcingwith MongoDB and PHP

Page 2: CQRS and Event Sourcing with MongoDB and PHP

About me

Davide Bellettini● Developer at Onebip● TDD addicted

@SbiellONE — about.bellettini.me

Page 3: CQRS and Event Sourcing with MongoDB and PHP

What is this talk about

Page 4: CQRS and Event Sourcing with MongoDB and PHP

A little bit of context

Page 5: CQRS and Event Sourcing with MongoDB and PHP

About OnebipMobile payment platform.Start-up born in 2005, acquired by Neomobile group in 2011.

Onebip today:- 70 countries- 200+ carriers- 5 billions potential users

Page 6: CQRS and Event Sourcing with MongoDB and PHP

LAMP stack

It all started with a Monolith

Page 7: CQRS and Event Sourcing with MongoDB and PHP

self-contained services communicating via REST

To a distributed system

Page 8: CQRS and Event Sourcing with MongoDB and PHP

First class modern NoSQL distributed dbs

Modern services

Page 9: CQRS and Event Sourcing with MongoDB and PHP

But the Monolith is still there

Page 10: CQRS and Event Sourcing with MongoDB and PHP

The problem

A reporting horror story

Page 11: CQRS and Event Sourcing with MongoDB and PHP

We need three new reports!

― Manager

Sure, no problem!

Page 12: CQRS and Event Sourcing with MongoDB and PHP

Deal with the legacy SQL schema

Page 13: CQRS and Event Sourcing with MongoDB and PHP
Page 14: CQRS and Event Sourcing with MongoDB and PHP

Deal with MongoDB

Page 15: CQRS and Event Sourcing with MongoDB and PHP

A little bit of queries here,a little bit of map-reduce there

Page 16: CQRS and Event Sourcing with MongoDB and PHP

1 month later...

Page 17: CQRS and Event Sourcing with MongoDB and PHP

Reports are finally ready!

Page 18: CQRS and Event Sourcing with MongoDB and PHP

until...

Page 19: CQRS and Event Sourcing with MongoDB and PHP

Your queries are killing production!

― SysAdmin

Page 20: CQRS and Event Sourcing with MongoDB and PHP

Still not enough!

Heavy query optimization,adding indexes

Page 21: CQRS and Event Sourcing with MongoDB and PHP

Let’s reuse data from other reports(don’t do that)

Page 22: CQRS and Event Sourcing with MongoDB and PHP

DB is ok, reports delivered.

Page 23: CQRS and Event Sourcing with MongoDB and PHP

but then...

Page 24: CQRS and Event Sourcing with MongoDB and PHP

Houston, we have a problem. Reports are not consistent (with other reports)

― Business guy

Page 25: CQRS and Event Sourcing with MongoDB and PHP
Page 26: CQRS and Event Sourcing with MongoDB and PHP

Mistakesweremade

Page 27: CQRS and Event Sourcing with MongoDB and PHP

Lessonslearned

Page 28: CQRS and Event Sourcing with MongoDB and PHP

It’s hard to compare different data in a distributed system splitted across multiple domains

#1Avoid multiple sources of truth

Page 29: CQRS and Event Sourcing with MongoDB and PHP

Same words, different concepts across domains

#2Ubiquitous language

Page 30: CQRS and Event Sourcing with MongoDB and PHP

Changing a report shouldn’t have side effects

#3Fault tolerance to change

Page 31: CQRS and Event Sourcing with MongoDB and PHP

Most common solutions

Page 32: CQRS and Event Sourcing with MongoDB and PHP

#1ETL + Map-Reduce

Page 33: CQRS and Event Sourcing with MongoDB and PHP

#2

Data Warehouse + Consultants

Page 34: CQRS and Event Sourcing with MongoDB and PHP

#3Mad science (Yeppa!)

Page 35: CQRS and Event Sourcing with MongoDB and PHP

What we wanted

Page 36: CQRS and Event Sourcing with MongoDB and PHP

No downtime in production

Consistent across domains

Must have

Page 37: CQRS and Event Sourcing with MongoDB and PHP

A system elastic enough to extract any metric

Real time data

Nice to have

Page 38: CQRS and Event Sourcing with MongoDB and PHP

In DDD we found the light

Page 39: CQRS and Event Sourcing with MongoDB and PHP

CQRS and Event Sourcing

Page 40: CQRS and Event Sourcing with MongoDB and PHP

Command-query responsibility segregation

(CQRS)

Page 41: CQRS and Event Sourcing with MongoDB and PHP
Page 42: CQRS and Event Sourcing with MongoDB and PHP

Commands

Anything that happens in one of your domains is triggered by a command and generates one or more events.

Order received -> payment sent -> Items queued

-> Confirmation email sent

Page 43: CQRS and Event Sourcing with MongoDB and PHP

Query

Generate read models from events depending how data need to be actually used (by users and other application internals)

Page 44: CQRS and Event Sourcing with MongoDB and PHP

Event SourcingThe fundamental idea of Event Sourcing is that of ensuring every change to the state of an application is captured in an event object, and that these event objects are themselves stored in the sequence they were applied.

― Martin Fowler

Page 45: CQRS and Event Sourcing with MongoDB and PHP

Starting from the beginning of time, you are literally unrolling history to reach state in a

given time

Unrolling a stream of events

Page 46: CQRS and Event Sourcing with MongoDB and PHP

Idea #1

Every change to the state of your application is captured in event object.

“UserLoggedIn”, “PaymentSent”, “UserLanded”

Page 47: CQRS and Event Sourcing with MongoDB and PHP

Idea #2

Events are stored in the sequence they were applied inside an event store

Page 48: CQRS and Event Sourcing with MongoDB and PHP

Idea #3

Everything is an event. No more state.

Page 49: CQRS and Event Sourcing with MongoDB and PHP

Idea #4

One way to store data/events but potentially infinite ways to read them.

A practical exampleTech ops, business control, monitoring, accounting they all are interested in reading data from different views.

Page 50: CQRS and Event Sourcing with MongoDB and PHP

Healthy NoSQL

Page 51: CQRS and Event Sourcing with MongoDB and PHP

You start with this{ "_id": ObjectId("123"), "username": "Flash", "city": …, "phone": …, "email": …,}

Page 52: CQRS and Event Sourcing with MongoDB and PHP

The more successful your company is, the more people

The more people, the more views

Page 53: CQRS and Event Sourcing with MongoDB and PHP

With documental dbs it's magically easy to add new fields to your collections.

Page 54: CQRS and Event Sourcing with MongoDB and PHP

Soon you might end up with{

"_id": ObjectId("123"),

"username": "Flash",

"city": …,

"phone": …,

"email": …,

"created_at": …,

"updated_at": …,

"ever_tried_to_purchase_something": …,

"canceled_at": …,

"acquisition_channel": …,

"terminated_at": …,

"latest_purchase_date": …,

}

Page 55: CQRS and Event Sourcing with MongoDB and PHP

A bomb waiting to detonate

It’s impossible to keep adding state changes to your documents and then expect to be able to extract them with

a single query.

Page 56: CQRS and Event Sourcing with MongoDB and PHP

Exploring Tools

Page 57: CQRS and Event Sourcing with MongoDB and PHP

Event Store

● Engineered for event sourcing● Supports projections● By the father of CQRS (Greg Young)● Great performanceshttp://geteventstore.com/

The badBased on Mono, still too unstable.

Page 58: CQRS and Event Sourcing with MongoDB and PHP

LevelWHEN

An event store built with Node.js and LevelDB● Faster than light● Completely custom, no tools to handle

aggregates

https://github.com/gabrielelana/levelWHEN

Page 59: CQRS and Event Sourcing with MongoDB and PHP

The known path

● PHP (any other language would just do fine)

● MongoDB 2.2.x

Page 60: CQRS and Event Sourcing with MongoDB and PHP

Why MongoDB

Events are not relational

Scales well

Awesome aggregation framework

Page 61: CQRS and Event Sourcing with MongoDB and PHP

Hands on

Page 62: CQRS and Event Sourcing with MongoDB and PHP

Storing Events

Page 63: CQRS and Event Sourcing with MongoDB and PHP

Service |

\ |

\ [event payload] |

\ |

Service --- Queue System <------------> API -> MongoDB

/ |

/ [event payload] |

/ |

Service |

The write architecture

Page 64: CQRS and Event Sourcing with MongoDB and PHP

Queues

Recruiter - https://github.com/gabrielelana/recruiter

Page 65: CQRS and Event Sourcing with MongoDB and PHP

MongoDB replica set

A MongoDB replica set with two logical dbs:

1. Event store where we would store events2. Reporting DB where we would store

aggregates and final reports

Page 66: CQRS and Event Sourcing with MongoDB and PHP

Anatomy of an event 1/2{ '_id' : '3318c11e-fe60-4c80-a2b2-7add681492d9', 'type': 'an-event-type', 'data': { 'meta' : { … }, 'payload' : { … } }}

Page 67: CQRS and Event Sourcing with MongoDB and PHP

Anatomy of an event 2/2'meta' : { 'creation_date': ISODate("2014-21-11T00:00:01Z"), 'saved_date': ISODate("2014-21-11T00:00:02Z"), 'source': 'some-bounded-context', 'correlation_id': 'a-correlation-id'},'payload' : { 'user_id': '1234', 'animal': 'unicorn', 'colour': 'pink', 'purchase_date': ISODate("2014-21-11T00:00:00Z"), 'price': '20/fantaeuros'}

Page 68: CQRS and Event Sourcing with MongoDB and PHP

Don’t trust the network: Idempotence{

'_id' : '3318c11e-fe60-4c80-a2b2-7add681492d9',

}

The _id field is actually defined client side and ensures idempotence if an event is received two times

Page 69: CQRS and Event Sourcing with MongoDB and PHP

Indexes

● Events collection is huge (~100*N documents)

● Use indexes wisely as they are necessary yet expensive

● With suggested event structure:{‘data.meta.created_at’: 1, type:1}

Page 70: CQRS and Event Sourcing with MongoDB and PHP

Benchmarking

How many events/second can you store?

Our machines were able to store roughly 150 events/sec. This number can be greatly increased with dedicated IOPS, more aggressive inserting policies, etc...

Page 71: CQRS and Event Sourcing with MongoDB and PHP

Final tips

● Use SSD on your storage machines

● Pay attention to write concerns (w=majority)

● Test your replica set fault tolerance

Page 72: CQRS and Event Sourcing with MongoDB and PHP

From eventsto meaningful metrics

Page 73: CQRS and Event Sourcing with MongoDB and PHP

Sequential Projector -> Event Mapper -> Projection -> Aggregation

The event processing pipeline

Page 74: CQRS and Event Sourcing with MongoDB and PHP

A real life problem

What is the conversion rate of our registered users?

Page 75: CQRS and Event Sourcing with MongoDB and PHP

#1 The registration event{

'_id' : '3318c11e-fe60-4c80-a2b2-7add681492d9',

'type': 'user-registered',

'data': {

'meta' : {

'save_date': ISODate("2014-21-11T00:00:09Z"),

'created_at': ISODate("2014-21-11T00:00:01Z"),

'source': 'core-domain',

'correlation_id': 'user-123456'

},

'payload' : {

'user_id': 123,

'username': 'flash',

'email': '[email protected]',

'country': 'IT'

}

}

}

Page 76: CQRS and Event Sourcing with MongoDB and PHP

#2 The purchase event{

'_id' : '3318c11e-fe60-4c80-a2b2-7add681492d9',

'type': 'user-purchased',

'data': {

'meta' : {

'save_date': ISODate("2014-21-11T00:10:09Z"),

'created_at': ISODate("2014-21-11T00:10:01Z"),

'source': 'payment-gateway',

'correlation_id': 'user-123456'

},

'payload' : {

'user_id': 123,

'email': '[email protected]',

'amount': 20,

'value': EUR,

'payment': 'credit_card',

'item': 'fluffy cat'

}

}

}

Page 77: CQRS and Event Sourcing with MongoDB and PHP

Sequential projector 1/2

[]->[x]->[]->[x]->[]->[]->[]->[]

|--------------| |------------|

|

|

|

|

---> Projector

Divides the stream of events into batches, filters events by type and pass those of interest to the mapper

Page 78: CQRS and Event Sourcing with MongoDB and PHP

Sequential projector 2/2

● It’s a good idea to select fixed sizes batches to avoid memory problems when you load your Cursor in memory

● Could be a long-running process selecting events as they arrive in realtime

Page 79: CQRS and Event Sourcing with MongoDB and PHP

Event mapper 1/3

Translates event fields to the Read Model domain

Takes an event as input, applies a bunch of logic and will return a list of Read Model fields.

Page 80: CQRS and Event Sourcing with MongoDB and PHP

Event mapper 2/3

Input event:user-registered

Output:$output = [

'user_id' => 123, // simply copied

'user_name' => 'flash', // simply copied

'email' => '[email protected]', // simply copied

'registered_at' => "2014-21-11T00:00:01Z" // From the data.meta.created_at event field

];

Page 81: CQRS and Event Sourcing with MongoDB and PHP

Event mapper 3/3

Input event:user-purchased

Output:$output = [

'user_id' => 123, // simply copied

'email' => '[email protected]', // simply copied

'purchased_at': "2014-21-11T00:10:01Z" // From the data.meta.created_at event field

];

Page 82: CQRS and Event Sourcing with MongoDB and PHP

Projection

Essentially it is your read model.The data that the business is interested in.

Page 83: CQRS and Event Sourcing with MongoDB and PHP

The Projection after event #1

db.users_conversion_rate_projection.findOne()

{

'user_id': 123,

'user_name': 'flash',

'email': '[email protected]',

'registered_at': "2014-21-11T00:00:01Z"

}

Page 84: CQRS and Event Sourcing with MongoDB and PHP

The Projection after event #2

{

'user_id': 123,

'user_name': 'flash',

'email': '[email protected]',

'registered_at': "2014-21-11T00:00:01Z"

'purchased_at': "2014-21-11" // Added this field and rewrote others

}

Page 85: CQRS and Event Sourcing with MongoDB and PHP

The Projection collection{

'user_id': 123,

'user_name': 'flash',

'email': '[email protected]',

'registered_at': "2014-21-11",

'purchased_at': "2014-21-11" // Added this field and rewrote others

}

{

'user_id': 456,

'user_name': 'batman',

'email': '[email protected]',

'registered_at': "2014-21-11",

'purchased_at': "2014-21-11" // Added this field and rewrote others

}

{

'user_id': 789,

'user_name': 'superman',

'email': '[email protected]',

'registered_at': "2014-21-12",

'purchased_at': "2014-21-12" // Added this field and rewrote others

}

Page 86: CQRS and Event Sourcing with MongoDB and PHP

The Projection - A few thoughts

Note that we didn't copy from events to projection all the available fields. Just relevant ones.

Page 87: CQRS and Event Sourcing with MongoDB and PHP

From these two events we could have generated infinite read models such as

● List all purchased products and related amounts for the company buyers

● Map all sales and revenues for our accounting dept

● List transactions for the financial department

Page 88: CQRS and Event Sourcing with MongoDB and PHP

One way to write,infinite ways to read!

Page 89: CQRS and Event Sourcing with MongoDB and PHP

The aggregation (1) - Total registered users

var registered = db.users_conversion_rate_projection.aggregate([

{

$match: {

"registered_at": { $gte: ISODate("2015-11-21"), $lte: ISODate("2015-11-22") }

}

},

{

$group: {

_id: { },

count: { $sum:1 }

}

}

]);

Page 90: CQRS and Event Sourcing with MongoDB and PHP

The aggregation (2) - User with a purchase

var purchased = db.users_conversion_rate_projection.aggregate([

{

$match: {

"registered_at": { $gte: ISODate("2015-11-21"), $lte: ISODate("2015-11-22") },

"purchased_at": { $exists: true }

}

},

{

$group: {

_id: { },

count: { $sum:1 }

}

}

]);

Page 91: CQRS and Event Sourcing with MongoDB and PHP

The aggregation (3) - Automate all the things

● You can easily create the aggregation framework statement by composition abstracting the concept of Column.

● This way you can dynamically aggregate your projections on (for example) an API requests.

● If your Projector is a long running process, your projections will be updated to the second and you automagically get realtime data.

Page 92: CQRS and Event Sourcing with MongoDB and PHP

Another events usage:Business & Tech Monitoring

Page 93: CQRS and Event Sourcing with MongoDB and PHP

Beware of the beast!No Silver Bullet

Page 94: CQRS and Event Sourcing with MongoDB and PHP

Events are expensiveThey require a lot of TIME to be parsed

Page 95: CQRS and Event Sourcing with MongoDB and PHP

Events are expensiveYou will end up with this billion size collection

(and counting)

Page 96: CQRS and Event Sourcing with MongoDB and PHP

Fixing wrong events is painful

Page 97: CQRS and Event Sourcing with MongoDB and PHP

Events are complex

Page 98: CQRS and Event Sourcing with MongoDB and PHP

Moving around events is horribly painful

Page 99: CQRS and Event Sourcing with MongoDB and PHP

Actually it will make your life incredibly difficult with hidden bugs and leaking

documentation.

Mongo won’t help you

Page 100: CQRS and Event Sourcing with MongoDB and PHP

Improvements

● Upgrade from MongoDB 2.2.x to 3.0.x● Switch to WiredTiger storage engine to save

space

Page 101: CQRS and Event Sourcing with MongoDB and PHP

Credits

Based on a talk by Jacopo Nardiello

● Slides: http://bit.ly/es-nardiello-2014 ● Video: https://vimeo.com/113370688

Page 102: CQRS and Event Sourcing with MongoDB and PHP

Q&A

Page 103: CQRS and Event Sourcing with MongoDB and PHP

@SbiellONE — about.bellettini.me

Thank you!