- Alice: add apple
- Server: apple added
- Alice: remove oranges
* Alice message gets delayed *
- Bob: remove oranges
- Server: oranges removed
* Alice message finally comes to the server*
- Server: oranges (already) removed
{
"_ServerVersion": 0,
"_ClientVersion$": 0,
"Ingredients": [
{
"name": "Juicy Orange",
"amount": 3
},
{
"name": "Juicy Apple",
"amount": 2
},
{
"name": "Banana",
"amount": 1
}
],
"TotalCost": 60,
...
}Alice is adding new Juicy Apple to the Ingredients list:
[{"op": "test", "path": "/_ServerVersion", "value": 0},
{"op": "replace", "path": "/_ClientVersion$", "value": 1},
{"op": "replace", "path": "/Ingredients/1/amount$", "value": "3"}]Server updates the DB , and responds: [{"op": "test", "path": "/_ClientVersion", "value": 1}, {"op": "replace", "path": "/_ServerVersion$", "value": 1}, {"op": "replace", "path": "/TotalCost", "value": 70}]
and clicks to remove Oranges from list
[{"op": "test", "path": "/_ServerVersion", "value": 1},
{"op": "replace", "path": "/_ClientVersion$", "value": 2},
{"op": "replace", "path": "/Ingredients/0/amount$", "value": "0"}] //instead of Delete = nullBob initial state
{
"_ServerVersion": 0,
"_ClientVersion$": 0,
"Ingredients": [
{
"name": "Juicy Orange",
"amount": 3
},
{
"name": "Juicy Apple",
"amount": 3
},
{
"name": "Banana",
"amount": 1
}
],
"TotalCost": 100,
...
}and remove Oranges
[{"op": "test", "path": "/_ServerVersion", "value": 0},
{"op": "replace", "path": "/_ClientVersion$", "value": 1},
{"op": "replace", "path": "/Ingredients/0/amount$", "value": "0"}] //instead of Delete = null
Server updates the DB and responds with
[{"op": "test", "path": "/_ClientVersion", "value": 1},
{"op": "replace", "path": "/_ServerVersion$", "value": 1},
{"op": "replace", "path": "/TotalCost", "value": 40}]
[{"op": "test", "path": "/_ServerVersion", "value": 1},
{"op": "replace", "path": "/_ClientVersion$", "value": 2},
{"op": "replace", "path": "/Ingredients/0/amount$", "value": "0"}] //instead of Delete = null
JSON we are talking about is Alice View-Model state, stored in session, not the Database state
Therefore _ServerVersion and _clientVersion matches for both peers (Alice and Server).
Server (App code) matches /Ingredients/0 with real Object to find out that there is no such thing in DB, so it performs app specific logic as for attempt to remove non-existing entry.
It could either
- do nothing, but just update
TotalCostthat changed in meantime.
[{"op": "test", "path": "/_ClientVersion", "value": 2},
{"op": "replace", "path": "/_ServerVersion$", "value": 2},
{"op": "replace", "path": "/TotalCost", "value": 40}]
- return error,
- or just a message to be shown to the user:
[{"op": "test", "path": "/_ClientVersion", "value": 2},
{"op": "replace", "path": "/_ServerVersion$", "value": 2},
{"op": "replace", "path": "/Notification$", "value": "There is no such thing in Receipt (or if app's server-side code is sophisticated enough, tp keeps track of modifications in recipes: Bob have already removed it) "},
{"op": "replace", "path": "/TotalCost", "value": 40}]