Reactive e-commerce API design
by Oleg Ilyenko / @easyangel
About me
-
8 years of software development
- 5 years working on e-commerce products
-
Creator of Scaldi - Scala DI library
-
Author of Hacking Scala blog
-
Scala Backend Engineer at commercetools GmbH
RESTful API design
-
Resources
- Where to put the boundary?
-
HTTP verbs
-
Transactions
Single Model
Separate Read/Write Model
CQRS
Command Query Responsibility Segregation
- Multiple read models
-
Precise and flexible write model
POST /my-shop/orders
{
"customerId": "456",
"lineItems": [
{"productId": "PRODUCT-1", "quantity": 3},
{"productId": "PRODUCT-25", "quantity": 2}
],
...
}
POST /my-shop/carts/CART-123
{
"actions": [
{"action": "addLineItem", "productId": "PRODUCT-1", "quantity": 5},
{"action": "removeLineItem", "productId": "PRODUCT-1", "quantity": 2},
{"action": "addLineItem", "productId": "PRODUCT-25", "quantity": 2}
]
}
POST /my-shop/orders
{
"cartId": "CART-123"
}
Characteristics
- ⇧ Change history
-
⇧ Much less data to transfer
- ⇧ More control over the changes
- ⇩ Not very suitable for data synchronization
Event Sourcing
Event Sourcing
-
Events represent facts from the past
- OrderCreated
- ProductNameChanged
- After event is saved, all business logic is already performed
- Most normalized form of the data
- ⇧ Point-in-time restore/view
- ⇧ Scale reads and writes separately
Multiple Read Models
Disadvantages of Event Sourcing
- ⇩ Bigger database
- ⇩ Events need to be backwards compatible
- ⇩ Eventual consistency
-
Where to put the boundary?
-
Should line item be part of order resource?
-
Or maybe it should be a separate resource?
Transactional Boundary
“We will argue below why we conclude that atomic
transactions cannot span entities. The programmer
must always stick to the data contained inside a single
entity for each transaction. This restriction is true for
entities within the same application and for entities
within different applications.”
Optimistic Concurrency Control
Bigger workflows
The Reactive Manifesto
Event API
GET /my-shop/events
HTTP/1.1 200 OK
Content-Type: text/event-stream
...
id: EVENT-123
data: {
"entity": "product",
"entityId": "PROD-56",
"type": "nameChanged",
"sequenceNumber": 10,
"payload": {...}
}
Implementation
-
SSE (Server-sent events)
- WebSockets
- Long-polling
Important properties
-
Event ID
- Can be: entityId + entityVersion
- Type of entity
- ID of entity
- Sequence number within this entity