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
, whereA
is: Bikes withgroup_id==X
andis_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
, whereA
is: Bikes withgroup_id==X
andis_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?