Matt Bishop
Principal Architect, Elastic Path
● Built extensible hypermedia API for commerce (“Cortex”)
● Run R&D team creating an event-driven commerce architecture
@MattBishopL3
CQRS, Events and Hypermedia
API Projections
Event-Driven Meetup, March 2019
CQRS
Command-Query Responsibility
Segregation
Domains have two separate
concerns:
● Data used to modify (Command)
● Data it provides (Query)
Automobile Domain Object
changeGear(Gear.3rd)
applyBrakes(FootPressure.HARD)
pressGasPedal(FootPressure.HARD)
readSpeedometer()
refuel(40, “litres”)
readFuelGauge()
Not your Daddy’s DAO
changeGear(Gear.3rd)
applyBrakes(FootPressure.HARD)
pressGasPedal(FootPressure.HARD)
readSpeedometer()
data IN != data OUT
→ 90 mph
Events
Immutable Records of Change
Past-tense description of what
happened
● Cannot be deleted, modified
● “What’s done is done”
Product of a Command
Automobile Commands
“command”: “change-gear”,
“gear”: “3rd”
readSpeedometer()
readFuelGauge()
“command”: “refuel”
“litres”: “40”
“command”: “apply-brakes”
“pressure”: “HARD”
“command”: “press-gas-pedal”
“pressure”: “HARD”
Commands produce Events
“event”: “gear-changed”,
“gear”: “3rd”
readSpeedometer()
readFuelGauge()
“event”: “refueled”
“litres”: “35”
“event”: “brakes-applied”
“pressure”: “HARD”
“event”: “gas-pedal-pressed”
“pressure”: “HARD”
NOTE: command asked for 40 litres
Projections
Interpretive Renderings of Events
Perspective → end consumer of state
Projections how clients “Query” the
state of the system
Projections
Subscribe to multiple events
Present an interpreted view of those events over time
Eventually consistent
Speedometer Projection Quiz
“event”: “engine-stopped”
…
“event”: “gas-pedal-pressed”,
“pressure”: “HARD”
GET /car/27/speedometer → ?? mph
REST
The document-centric way
Data endpoints with CRUDdy
methods
HTTP verbs:
● GET (read)
● POST (create)
● PUT / PATCH (modify)
● DELETE (uh, delete)
Most REST APIs are really
Remote Procedure Calls
Netflix’s REST Problem
A Cross-Cultural Misunderstanding
Netflix Had an RPC-Style REST API
● Grew organically
● Nobody loved it
● Slow performance
● Hard to modify
Netflix Client teams suffered greatly
API Clients made numerous chained calls, using data from a previous call to make the next calls
Netflix API team wanted to move chaining to server-side for efficiency
Dynamic Scripting Framework
● Client teams write
their own APIs
(Endpoint code)
● Provided a JVM-based
platform
● JS developers are
scripters, right?
This pattern is known now
as “Backend For
Frontend”
Backends For Frontends (BFFs)
The Microservice Client Integration Pattern
What’s a BFF?
● Server-Side, Client-facing microservice to
orchestrate the other microservices
● Services one kind of client
● BFFs are owned by the client team
● https://samnewman.io/patterns/architectural/bff
/
Typical shared API model
● Multiple teams “share”
the API team
● Causes conflict,
coordination costs
● BFF gives the client teams
the right to create their
own API
● No sharing, faster
● BFF is recursive; split BFFs
as needed to avoid
conflict
● Biggest problem is unified
experiences. BFFs need to
deliver the same rules and
offerings as users interact
with multiple clients
All these BFFs Produce
Basically the Same Thing
The Netflix Client Team Revolt
https://netflix.github.io/falcor/
● Netflix client team did not
want to write BFFs
● Jafar Hussain published
Falcor to give clients a
shared API that was
query-based, not RPC
GET /model.json?paths=["user.name", "user.surname", "user.address"]
{
user: {
name: "Claire",
surname: "Underwood",
address: "1600 Pennsylvania Avenue, Washington, DC”
}
}
Zoom into diagram to see model.json...
Falcor’s
Backend is a
Graph Netflix UI teams wants to interact
with navigable, related data graph
What is a Graph?
● A bunch of things (nodes, vertices)
● Connected to each other (edges)
● The edges are named (relationship)
● Navigate the graph by following the relationships
Another Client-Centric Graph Technology
Graphs are the Information
Modeling Paradigm for
Humans
Graph Queries
are Good, but...
What about Commands?
Falcor is query-only
GraphQL has Mutations, but no
context for when they can be used
They are just a list of Actions
GraphQL is missing State
State is More Than Data
GraphQL and Falcor are
data graphs, not state
graphs
State is data +
contextual affordances
Clients need to know
when they can use an
affordance
Hypermedia
Navigation and Stateful
Affordances in one API
● Hypermedia presents a graph
● Resources linked together with
relationships
● Affordances appear as links
when relevant, disappear when
not
How To Use a Hypermedia API
1. GET `/`
2. Follow <link>s to related resources
3. DELETE what you don’t want
4. PUT what you want to change
5. POST what you want to create
6. Go to Step 2
Events to Hypermedia Projections
Design the Hypermedia API First
Model API in a Graph Database (or RDBMS)
Bind API to a Graph Database
/investors/id
/rounds/id
<link rel=”participated-
in”>
Create Event Projectors For Related Nodes
payment
applied
/orders/id1
/payments/id2
order
[id1]
paymen
t [id2]
Payment
Projector ƒ
1. create
payment node
2. create
“payment” edge <link rel=”payment”>
Create Event Projectors For Affordances
payment
applied
/orders/id1
/checkout/orders/id1
order
[id1]
Checkout
Projector ƒ
2. create
“checkout” edge <link rel=”checkout”>
Checkout
order
[id1]
1. create
checkout
affordance node
related
event
related
event
related
event
related
event
Bind PUT/POST/DELETE to Commands
POST
/checkout/orders/id1
/checkout/orders/id1
checkout
order
● Map POST / PUT / PATCH / DELETE
to related Command
● ID data in URL used to find action
node in DB, fills in Command
Projector ƒ
node
[id1]
node [id2]
/node/id1
/node/id2
<link rel=”name”>
PUT / POST / DELETE
Full Circle
Events
Commands
Thank You!
@MattBishopL3

Hypermedia APIs from Event-Driven CQRS Systems