I have following model:
class Bikes:
id: int # primary key
group_id: int # for split/merge (see below)
quantity: int # number of items
is_broken: bool # true if all are broken
repair_request_id: int # reference to some RepairReqest (if broken).
repair_request_id is set to some RepairRequest object that describes a request for repair quantity of Bikes. is_broken must be true if repair_request_id is set.
My RESTful API for it:
GET /bikes # returns list
GET /bikes/:id # returns single instance
PUT /bikes/:id # updates existing or adds new instance on list, returns instance data
With PUT I can mark Bikes as broken by setting is_broken=true and repair_request_id=R.
Now I need to add an operation that allow to add RepairRequest for part of bikes with group_id=X by splitting a single instance in two.
Assuming that I want to add a repair request for quantity=Q bikes from group_id=X I need to do following steps, in single transaction:
- Find
A, whereAis: Bikes withgroup_id==Xandis_broken==false - Create
B, setquantity=Q, is_broken=true - in
A, SetA.quantity = A.quantity - Q - Save all
This creates new instance and modifies original one. There is a requirement that I can have only one instance with group_id=X and is_broken=false, so when Bikes B are repaired they are merged back with following steps, in single transaction:
- Find
A, whereAis: Bikes withgroup_id==Xandis_broken==false - If not found, update B (
is_broken=false, repair_request_id=null) - If found:
- Set
A.quantity = A.quantity+B.quantity - Delete B
- Set
Merge can be done with PUT /bikes/:id. This could return a different instance that was sent, but this is fine for me.
Problem is with split operation. My requirements are:
- Backend should decide if it needs to do split or just call the same handler that was created for HTTP PUT.
- I do not want to expose transactions in REST API - one HTTP request is a single transaction.
My current idea:
POST /bikes/1/operations/set_broken
payload:
{
"quantity": Q
}
response (after split):
[
{
"id": 1,
[...]
"is_broken": false
},
{
"id": 2,
[...]
"is_broken": true
}
]
Do you think it is clear? Or do you know a better design pattern for this scenario?