What this is about
A request often writes more than one object: a company with new employees, an invoice with its line items, a delete that takes dependent children along. Each of these objects gets its own hooks, that is the hooks of its model, with its operation.
CDMS does not run these hooks right away. During the recursion it puts each object into two queues, one for the before hooks and one for the after hooks. A queue is a list that is processed in the order the entries were added. Only when the whole tree has been walked through does CDMS process them.
Which child gets which hooks
When: child without id, the relation has CREATE
-
1CDMScreates the child through the system layer of its model
-
2Hookbefore and after hooks of the child model with
CREATE
Result: This is the same for create, PUT and PATCH of the parent: the new child always gets CREATE.
When: child with id, the relation has UPDATE; with PATCH also every list entry with id
-
1CDMStransfers the sent values to the child
-
2Hookbefore and after hooks of the child model with
UPDATE(for create and PUT) orPATCH(for PATCH)
Result: Which children are changed along is described in The four cases in nested writing.
When: dependent child (relation with DELETE), when the parent is deleted or because it drops out of the relation with PUT or PATCH
-
1CDMSchecks the delete role of the child and takes it up for removal
-
2Hookbefore and after hooks of the child model with
DELETE -
3CDMSwalks through the relations of the child in the same way, across any number of levels
Result: See Dependent objects (cascades) and Deleting by changing.
When: relation without DELETE: the child drops out of the relation or the parent is deleted
-
1CDMS→Databaseonly clears the reference between the two objects
-
2Hookno hooks for the child
Result: The child stays. For CDMS this is not an operation on the child.
When: child with id, the relation has no UPDATE (with create, PUT and PATCH single references)
-
1CDMSonly sets the reference to the existing child
-
2Hookno hooks for the child
Result: The child itself is not changed.
The queue in an example
A department has a list employees. The relation has the flags CREATE, UPDATE and DELETE. Before the request, Anna and Ben belong to the department. The client sends a PUT:
PUT /api/rest/org/department/update/d-1
{
"data": {
"id": "d-1",
"name": "Vertrieb Nord",
"employees": [
{ "id": "e-anna", "name": "Anna Berg" },
{ "name": "Cem" }
]
},
"response": ["id"]
}200, { "data": { "id": "d-1" }, … }flowchart TB
D["Department d-1<br/>UPDATE"] --> A["Anna<br/>UPDATE"]
D --> C["Cem, new<br/>CREATE"]
D -.-> B["Ben, omitted<br/>DELETE"]
CDMS walks through the department and adds these entries to the queues:
-
1CDMSdepartment: before entry
UPDATE -
2CDMSBen is missing from the list and is dependent: before and after entry
DELETE, Ben is marked for removal -
3CDMSAnna: before entry
UPDATE, transfer values, after entryUPDATE -
4CDMSCem: before entry
CREATE, transfer values, after entryCREATE -
5CDMSdepartment done: after entry
UPDATE
Then CDMS processes the queues, per operation in the fixed order CREATE, UPDATE, PATCH, DELETE:
| Step | Before hooks | After hooks |
|---|---|---|
1 · CREATE | Cem | Cem |
2 · UPDATE | department, then Anna | Anna, then department |
3 · DELETE | Ben | Ben |
Validation and saving lie between the two columns, see The order within a write operation. Within one operation, the before hooks run in the order the entries were added, so the parent before its children. CDMS adds the after entries only once an object and its children are done. That is why the children run before the parent there.
The order of the operations takes precedence over the tree structure. In the example, the before hook of the new child Cem runs before the before hook of the department, because CREATE comes before UPDATE.
Cascade on delete
With DELETE there is only one operation. Here the tree alone decides the order:
flowchart TB
F["Company<br/>before 1 · after 5"] --> A1["Department North<br/>before 2 · after 3"]
A1 --> P1["Team N-1<br/>before 3 · after 1"]
A1 --> P2["Team N-2<br/>before 4 · after 2"]
F --> A2["Department South<br/>before 5 · after 4"]
F -.-> K["Customer, only unlinked<br/>no hook"]
The before hooks run from top to bottom, the after hooks from bottom to top. So in its after hook, the parent sees that all children have already been handled.
When the before hooks run, CDMS has already finished the recursion:
- Every dependent child has been taken out of the relation fields of its parent and marked for removal. The before hook of the company no longer finds its departments in its list. Instead, each department gets its own hook, with all its fields.
- The roles of all objects have been checked. If the delete role is missing for one child, not a single hook runs.
- A before hook that throws an exception prevents the deletion of the whole tree, no matter on which level it sits.
Decision table
| What happens to the object | Flag on the relation | Hooks of the object |
|---|---|---|
new, without id | CREATE | CREATE |
with id, values sent | UPDATE | UPDATE for create and PUT, PATCH for PATCH |
with id, only linked | no UPDATE | none |
| drops out of the relation or parent is deleted | DELETE | DELETE, also for its own dependent children |
| drops out of the relation or parent is deleted | no DELETE | none, only the reference is cleared |
When reading back: READ hooks
After a create, PUT or PATCH, CDMS reads the object back with the response of the request. Every expanded reference and every entry of an expanded list is a separate read through its own model, with its own READ hook. If the response in the example also requests employees, the READ hooks of the department and of every employee run. See Expanding references and lists.
Pitfalls
Where to go next
- Where the queues are processed in the flow: The order within a write operation
- Which children are deleted along and which are only unlinked: Dependent objects (cascades)
- What happens when the hook of a child throws: When a hook fails