Information System Model — Mutations & Avalanches¶
Multi-Context Mutations¶
The mechanism that governs Context membership and instance propagation is the propagationConstraint field embedded in every Relation instance. This field defines the “Avalanche Effect”—how the target Entity instance behaves when the source Resource instance (or the Relation instance itself) is added to or removed from a Context.
Authorization assumed: The examples in this chapter illustrate propagation only. Their Contexts (
C1,C2, …) are not placed in a specific tree, and every request is assumed to be issued by a role authorized for the source and the destination (for example anIS-Manager, withbypassHierarchyCheck=truewhere the destination lies outside its subtree and siblings). The authorization rules are in Membership Operations.
Strict Downstream Propagation: The “Avalanche Effect” travels strictly forwards along directed Relation instances (from source to target).
For
Add to Context: the outcome depends on the type of the explicitly added instance.
Resource instance: the Resource instance is added. Each of its outgoing IsRelatedTo and ConsistsOf instances is added if and only if its constraint is
add: propagate; since a Relation instance cannot exist without its target, it pulls its target Entity instance into the target Context as well, and the avalanche continues from there. Withadd: unpropagatethe IsRelatedTo or ConsistsOf instance is left behind (it is NOT added to the target Context), even if its target Entity instance happens to already exist in that target Context independently.Facet instance: a Facet instance cannot exist in a Context without the ConsistsOf instance pointing to it, so that ConsistsOf instance is added with it. This is a structural requirement, not upstream propagation: it stops at the ConsistsOf instance and never reaches its source. The source Resource instance must already be in the target Context; the system never pulls it, and the operation fails with
409 Conflictotherwise. No avalanche follows, since a Facet instance has no outgoing relations.IsRelatedTo instance: its source Resource instance must already be in the target Context; the system never pulls it, and the operation fails with
409 Conflictotherwise. The target Resource instance is always added, because an IsRelatedTo instance cannot exist without its target: theaddconstraint of the explicitly added IsRelatedTo instance is not consulted, since it governs only the avalanche that reaches that IsRelatedTo instance from its source. From the target Resource instance, the avalanche continues as for a Resource instance.ConsistsOf instance: its source Resource instance must already be in the target Context; the system never pulls it, and the operation fails with
409 Conflictotherwise. The target Facet instance is always added, because a ConsistsOf instance cannot exist without its target, whatever theaddconstraint of the explicitly added ConsistsOf instance.For
Remove from Context: This operation acts on a single Context. When evaluating constraints, the avalanche travels exclusively downstream. If a client removes a Relation instance, its source Resource instance remains completely untouched in that Context. Conversely, if a client removes a Resource instance, the system must drop all incoming Relation instances pointing to it to preserve graph integrity (a Relation instance cannot exist without its target). This drop is a strict topological cleanup, not a backwards avalanche: it does not trigger anyremoveconstraint evaluations on the sources of those incoming Relation instances.
To understand this powerful concept, we will build up the logic through simple didactic examples before demonstrating a comprehensive scenario.
Note: While the following examples illustrate an Add to Context or Remove from Context operation starting from a Resource instance, the same constraint rules govern the avalanche that continues from an explicitly added or removed IsRelatedTo or ConsistsOf instance, with one exception for Add to Context: the explicitly added Relation instance always brings its target, whatever its own add constraint, and its source must already be in the destination Context (see the per-type rules above). An operation starting directly on a Facet instance triggers no avalanche, since Facet instances have no outgoing relations; on Add to Context it brings only the ConsistsOf instance pointing to it, and its source Resource instance must already be in the destination Context.
Add Propagation¶
When an instance (e.g., a Resource instance) is added to a new Context (e.g., via an Add to Context operation), the system evaluates the add propagation constraint on all its visible outgoing Relation instances to determine if the target Entity instances (Resource instance or Facet instance) should follow.
The traversal is parameterized by propagationMode:
surgical: adds the explicitly targeted instance without the recursive traversal described here: the avalanche never continues through IsRelatedTo instances. What the targeted instance brings with it depends on its kind (Resource, Facet, IsRelatedTo, or ConsistsOf) and is defined inpropagationMode.shallow(default): propagation follows eligibleadd: propagateedges, but a path stops when its target Entity instance is already present in the destination Context. The existing target is treated as the boundary of that path.deep: propagation continues through eligible outgoing Relation instances even when an intermediate target Entity is already present, producing the full reachable transitive closure.
The examples below illustrate the propagation-constraint decisions shared by shallow and deep; where every traversed target is initially absent, both modes produce the same result.
Cycle Prevention (Depth-First Search): To prevent infinite traversal loops in
deepmode (especially whenpropagateconstraints form closed cycles in complex topologies), the model prescribes a Modified Depth-First Search (DFS) traversal equipped with a Visited Set mechanism. The traversal safely halts recursion upon encountering an instance already evaluated in the current avalanche (see Complex Topology Scenarios).
Initial State:
The scenario contains the following Resource instances:
Resource instance
R1exists in ContextC1.
The scenario contains the following Facet instances:
Facet instance
F1exists in ContextC1.Facet instance
F2exists in ContextC1.
The scenario contains the following ConsistsOf instances:
R1is attached toF1via ConsistsOf instanceCO11, which exists in ContextC1and is configured withadd: propagate.R1is attached toF2via ConsistsOf instanceCO12, which exists in ContextC1and is configured withadd: unpropagate.
---
config:
theme: neutral
flowchart:
edgeLabelBackground: "#ffffff"
---
graph LR
R1("<b>R1</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
F1("<b>F1</b> <br/> <sub>[<i>C1</i>]</sub>"):::facet
F2("<b>F2</b> <br/> <sub>[<i>C1</i>]</sub>"):::facet
R1 -->|"<b>CO11</b> <br/> add: propagate <br/> <sub>[<i>C1</i>]</sub>"| F1
R1 -->|"<b>CO12</b> <br/> add: unpropagate <br/> <sub>[<i>C1</i>]</sub>"| F2
classDef resource fill:#4f81bd,stroke:#385d8a,stroke-width:3px,color:#fff;
linkStyle default stroke:#385d8a,stroke-width:3px;
classDef facet fill:#9bbb59,stroke:#4f6228,stroke-width:3px,color:#fff;
After Add to Context C2:
If a user in Context C1 adds R1 into Context C2, CO11 and F1 follow R1 into the new Context, while CO12 and F2 stay behind. The resulting contextual projection of R1 in Context C2 is structurally different from its contextual projection in Context C1.
(Note: If the resulting topology in the target Context violates the Resource schema—for example, if an instance of F2’s Type was mandatory, or if the propagation would violate a max cardinality limit—the entire operation strictly fails and is aborted with a validation error! The system does not attempt partial merges or silent replacements; it strictly propagates the eligible elements and validates the final candidate graph).
---
config:
theme: neutral
flowchart:
edgeLabelBackground: "#ffffff"
---
graph LR
R1("<b>R1</b> <br/> <sub>[<i>C1</i>, <i>C2</i>]</sub>"):::resource
F1("<b>F1</b> <br/> <sub>[<i>C1</i>, <i>C2</i>]</sub>"):::facet
F2("<b>F2</b> <br/> <sub>[<i>C1</i>]</sub>"):::facet
R1 -->|"<b>CO11</b> <br/> add: propagate <br/> <sub>[<i>C1</i>, <i>C2</i>]</sub>"| F1
R1 -->|"<b>CO12</b> <br/> add: unpropagate <br/> <sub>[<i>C1</i>]</sub>"| F2
classDef resource fill:#4f81bd,stroke:#385d8a,stroke-width:3px,color:#fff;
linkStyle default stroke:#385d8a,stroke-width:3px;
classDef facet fill:#9bbb59,stroke:#4f6228,stroke-width:3px,color:#fff;
Remove Propagation¶
Visual Disclaimer (Infrastructure Topology Focus): To avoid unreadable diagrams, the following simplified scenarios focus exclusively on the Infrastructure Topology (the graph of Resource instances and IsRelatedTo instances). While omitted from these didactic diagrams to reduce noise, it is strictly understood that every valid Resource instance must encapsulate its own Descriptive Topology (its attached Facet instances).
To understand the difference between cascade, cascadeWhenOrphan, and keep during a removal operation, consider the following scenario strictly contained in Context C1.
Initial State:
The scenario contains the following Resource instances:
Resource instance
R1exists in ContextC1.Resource instance
R2exists in ContextC1.Resource instance
R3exists in ContextC1.Resource instance
R4exists in ContextC1.Resource instance
R5exists in ContextC1.Resource instance
R6exists in ContextC1.
The scenario contains the following IsRelatedTo instances:
R1is linked toR2via IsRelatedTo instanceIRT12, which exists in ContextC1and is configured withremove: cascadeWhenOrphan.R1is linked toR3via IsRelatedTo instanceIRT13, which exists in ContextC1and is configured withremove: cascadeWhenOrphan.R1is linked toR4via IsRelatedTo instanceIRT14, which exists in ContextC1and is configured withremove: cascade.R1is linked toR5via IsRelatedTo instanceIRT15, which exists in ContextC1and is configured withremove: keep.R5is linked toR4via IsRelatedTo instanceIRT54, which exists in ContextC1and is configured withremove: keep.R6is linked toR2via IsRelatedTo instanceIRT62, which exists in ContextC1and is configured withremove: keep.
---
config:
theme: neutral
flowchart:
edgeLabelBackground: "#ffffff"
---
graph LR
R1("<b>R1</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R6("<b>R6</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R2("<b>R2</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R3("<b>R3</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R4("<b>R4</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R5("<b>R5</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R1 -->|"<b>IRT12</b> <br/> remove: cascadeWhenOrphan <br/> <sub>[<i>C1</i>]</sub>"| R2
R1 -->|"<b>IRT13</b> <br/> remove: cascadeWhenOrphan <br/> <sub>[<i>C1</i>]</sub>"| R3
R1 -->|"<b>IRT14</b> <br/> remove: cascade <br/> <sub>[<i>C1</i>]</sub>"| R4
R1 -->|"<b>IRT15</b> <br/> remove: keep <br/> <sub>[<i>C1</i>]</sub>"| R5
R6 -->|"<b>IRT62</b> <br/> remove: keep <br/> <sub>[<i>C1</i>]</sub>"| R2
R5 -->|"<b>IRT54</b> <br/> remove: keep <br/> <sub>[<i>C1</i>]</sub>"| R4
classDef resource fill:#4f81bd,stroke:#385d8a,stroke-width:3px,color:#fff;
linkStyle default stroke:#385d8a,stroke-width:3px;
After removing R1 from Context C1:
When the user explicitly removes R1 from Context C1, the system evaluates the remove constraints on the outgoing IsRelatedTo instances:
On
R2(cascadeWhenOrphan):IRT12drops. The system checksR2and sees it still has another incoming IsRelatedTo instance in ContextC1(IRT62fromR6). Therefore,R2is “saved” and kept in ContextC1.On
R3(cascadeWhenOrphan):IRT13drops. The system checksR3and sees it has no other incoming IsRelatedTo instances in ContextC1. It is officially an orphan, soR3is removed from ContextC1.On
R4(cascade):IRT14drops. The constraint is a pure, strictcascade. The system doesn’t care that the Relation instanceIRT54was still pointing to it;R4is forcefully removed from ContextC1. As a topological consequence,IRT54also disappears. A Relation instance cannot exist in a Context if either its source or its target is missing:IRT15died because its source (R1) was removed, whereasIRT54dies because its target (R4) was removed. (Note on Cycles: Because the avalanche drops Relation instances from the Context as it progresses, any cycle in the graph is naturally broken when the first relation drops, preventing infinite topological loops).On
R5(keep):IRT15drops (because the sourceR1is gone). The constraint is explicitlykeep. The system leavesR5completely untouched, without even checking if it becomes an orphan.R5is kept in ContextC1.
The resulting topology in Context C1 demonstrates all behaviors at a glance:
---
config:
theme: neutral
flowchart:
edgeLabelBackground: "#ffffff"
---
graph LR
R6("<b>R6</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R2("<b>R2</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R5("<b>R5</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R6 -->|"<b>IRT62</b> <br/> remove: keep <br/> <sub>[<i>C1</i>]</sub>"| R2
classDef resource fill:#4f81bd,stroke:#385d8a,stroke-width:3px,color:#fff;
linkStyle default stroke:#385d8a,stroke-width:3px;
Recursive Avalanche¶
The removal avalanche is a recursive transaction. cascadeWhenOrphan evaluates the final state of the Context after the entire wave of deletions, not just the initial snapshot. Consider this advanced topology:
Initial State:
The scenario contains the following Resource instances:
Resource instance
R1exists in ContextC1.Resource instance
R2exists in ContextC1.Resource instance
R3exists in ContextC1.Resource instance
R4exists in ContextC1.
The scenario contains the following IsRelatedTo instances:
R1is linked toR2via IsRelatedTo instanceIRT12, which exists in ContextC1and is configured withremove: cascadeWhenOrphan.R1is linked toR4via IsRelatedTo instanceIRT14, which exists in ContextC1and is configured withremove: cascade.R4is linked toR2via IsRelatedTo instanceIRT42, which exists in ContextC1and is configured withremove: cascadeWhenOrphan.R4is linked toR3via IsRelatedTo instanceIRT43, which exists in ContextC1and is configured withremove: keep.
---
config:
theme: neutral
flowchart:
edgeLabelBackground: "#ffffff"
---
graph LR
R1("<b>R1</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R2("<b>R2</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R3("<b>R3</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R4("<b>R4</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R1 -->|"<b>IRT12</b> <br/> remove: cascadeWhenOrphan"| R2
R1 -->|"<b>IRT14</b> <br/> remove: cascade"| R4
R4 -->|"<b>IRT42</b> <br/> remove: cascadeWhenOrphan"| R2
R4 -->|"<b>IRT43</b> <br/> remove: keep"| R3
classDef resource fill:#4f81bd,stroke:#385d8a,stroke-width:3px,color:#fff;
linkStyle default stroke:#385d8a,stroke-width:3px;
What happens if we remove R1 from Context C1?
R1is removed. Its outgoing IsRelatedTo instances drop.IRT14drops withcascade.R4is forcefully removed from ContextC1.Because
R4is removed, its outgoing IsRelatedTo instances also drop in the same transaction avalanche!IRT43drops. The constraint iskeep, soR3safely remains in ContextC1.Now, look at
R2. BothIRT12andIRT42drop (both withcascadeWhenOrphan). Because both of its incoming IsRelatedTo instances are destroyed in this single avalanche,R2becomes a true orphan at the end of the transaction. Thus,R2is removed from ContextC1.
Resulting Graph:
Only R3 survives the avalanche.
---
config:
theme: neutral
flowchart:
edgeLabelBackground: "#ffffff"
---
graph LR
R3("<b>R3</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
classDef resource fill:#4f81bd,stroke:#385d8a,stroke-width:3px,color:#fff;
linkStyle default stroke:#385d8a,stroke-width:3px;
Add and Remove Asymmetry¶
Removing an instance from a Context is not always the exact inverse of adding it. Invertibility is strictly not guaranteed. The remove constraints dictate what happens to the target Entity instances, and depending on how they are paired with add constraints, applying an Add to Context followed by a Remove from Context (or vice versa) will likely not return the graph to its initial state. (Note: in the following visual examples we primarily illustrate propagation using a Resource instance, but the same mechanics apply when removing a Relation instance, e.g., an IsRelatedTo instance).
Sequence A: Add first, then Remove
| Propagation Constraint | Remove: cascade
(or cascadeWhenOrphan when orphan) | Remove: keep
(or cascadeWhenOrphan when not orphan) |
|————————|——————————————————————-|———————————————————————–|
| Add: propagate | Symmetrical Context Add/Remove | Add expands Context, Remove leaves targets in Context |
| Add: unpropagate | Add isolated, Remove cascades to targets | Symmetrical isolated Add/Remove |
Sequence B: Remove first, then Add
| Propagation Constraint | Add: propagate | Add: unpropagate |
|————————|———————-|————————|
| Remove: cascade
(or cascadeWhenOrphan when orphan) | Symmetrical Context Add/Remove | Add isolated, Remove cascades to targets |
| Remove: keep
(or cascadeWhenOrphan when not orphan) | Add expands Context, Remove leaves targets in Context | Symmetrical isolated Add/Remove |
To demonstrate this fundamental asymmetry, let’s explore two specular scenarios.
Scenario 1: add: propagate and remove: keep¶
Suppose we have an IsRelatedTo instance (IRT1) from R1 to R2 configured to propagate on add, but keep on remove.
1. Initial State:
The scenario contains the following Resource instances:
Resource instance
R1exists in ContextC1.Resource instance
R2exists in ContextC1.
The scenario contains the following IsRelatedTo instances:
R1is linked toR2via IsRelatedTo instanceIRT12, which exists in ContextC1and is configured withadd: propagateandremove: keep.
---
config:
theme: neutral
flowchart:
edgeLabelBackground: "#ffffff"
---
graph LR
R1("<b>R1</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R2("<b>R2</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R1 -->|"<b>IRT12</b> <br/> add: propagate <br/> remove: keep <br/> <sub>[<i>C1</i>]</sub>"| R2
classDef resource fill:#4f81bd,stroke:#385d8a,stroke-width:3px,color:#fff;
linkStyle default stroke:#385d8a,stroke-width:3px;
2. After Add (Add R1 to Context C2):
Adding R1 to Context C2 triggers the propagate constraint on IRT12. IRT12 and R2 are automatically pulled into Context C2 alongside R1.
---
config:
theme: neutral
flowchart:
edgeLabelBackground: "#ffffff"
---
graph LR
R1("<b>R1</b> <br/> <sub>[<i>C1</i>, <i>C2</i>]</sub>"):::resource
R2("<b>R2</b> <br/> <sub>[<i>C1</i>, <i>C2</i>]</sub>"):::resource
R1 -->|"<b>IRT12</b> <br/> add: propagate <br/> remove: keep <br/> <sub>[<i>C1</i>, <i>C2</i>]</sub>"| R2
classDef resource fill:#4f81bd,stroke:#385d8a,stroke-width:3px,color:#fff;
linkStyle default stroke:#385d8a,stroke-width:3px;
3. After Remove (Remove R1 from Context C2):
We now remove R1 from Context C2, attempting to revert our action. Because the constraint on IRT12 is keep, R2 is left untouched in Context C2. The final state does not match the initial state.
---
config:
theme: neutral
flowchart:
edgeLabelBackground: "#ffffff"
---
graph LR
R1("<b>R1</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R2("<b>R2</b> <br/> <sub>[<i>C1</i>, <i>C2</i>]</sub>"):::resource
R1 -->|"<b>IRT12</b> <br/> add: propagate <br/> remove: keep <br/> <sub>[<i>C1</i>]</sub>"| R2
classDef resource fill:#4f81bd,stroke:#385d8a,stroke-width:3px,color:#fff;
linkStyle default stroke:#385d8a,stroke-width:3px;
Scenario 2: add: unpropagate and remove: cascade¶
Suppose we have an IsRelatedTo instance (IRT12) configured to NOT propagate on add, but forcefully cascade on remove.
1. Initial State:
The scenario contains the following Resource instances:
Resource instance
R1exists in ContextsC1andC2.Resource instance
R2exists in ContextsC1andC2.
The scenario contains the following IsRelatedTo instances:
R1is linked toR2via IsRelatedTo instanceIRT12, which exists in ContextsC1andC2and is configured withadd: unpropagateandremove: cascade.
---
config:
theme: neutral
flowchart:
edgeLabelBackground: "#ffffff"
---
graph LR
R1("<b>R1</b> <br/> <sub>[<i>C1, C2</i>]</sub>"):::resource
R2("<b>R2</b> <br/> <sub>[<i>C1, C2</i>]</sub>"):::resource
R1 -->|"<b>IRT12</b> <br/> add: unpropagate <br/> remove: cascade <br/> <sub>[<i>C1, C2</i>]</sub>"| R2
classDef resource fill:#4f81bd,stroke:#385d8a,stroke-width:3px,color:#fff;
linkStyle default stroke:#385d8a,stroke-width:3px;
2. After Remove (Remove R1 from Context C2):
Removing R1 from Context C2 triggers the strict cascade on IRT12. IRT12 drops and R2 is forcefully removed from Context C2 as well.
---
config:
theme: neutral
flowchart:
edgeLabelBackground: "#ffffff"
---
graph LR
R1("<b>R1</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R2("<b>R2</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R1 -->|"<b>IRT12</b> <br/> add: unpropagate <br/> remove: cascade <br/> <sub>[<i>C1</i>]</sub>"| R2
classDef resource fill:#4f81bd,stroke:#385d8a,stroke-width:3px,color:#fff;
linkStyle default stroke:#385d8a,stroke-width:3px;
3. After Add (Add R1 back to Context C2):
We now add R1 back into Context C2, attempting to revert the removal. Because the constraint on IRT12 is unpropagate, R2 does not follow R1 into Context C2. Again, the final state is permanently altered from the initial state.
---
config:
theme: neutral
flowchart:
edgeLabelBackground: "#ffffff"
---
graph LR
R1("<b>R1</b> <br/> <sub>[<i>C1, C2</i>]</sub>"):::resource
R2("<b>R2</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R1 -->|"<b>IRT12</b> <br/> add: unpropagate <br/> remove: cascade <br/> <sub>[<i>C1</i>]</sub>"| R2
classDef resource fill:#4f81bd,stroke:#385d8a,stroke-width:3px,color:#fff;
linkStyle default stroke:#385d8a,stroke-width:3px;
Reference Counting¶
Removing an instance from a Context simply erases its presence from that specific Venn diagram, but it does not necessarily destroy the instance from the global Sea of Nodes of the infrastructure.
However, an instance has no reason to exist if it doesn’t belong to any Context. To prevent the system from filling up with orphaned, invisible instances, the Information System behaves as under a strict Reference Counting paradigm, where the Contexts an instance belongs to are its references. The term describes the observable behaviour, not a stored counter. An instance can be removed from a Context either explicitly or implicitly via a removal avalanche. When it no longer belongs to any Context (meaning it no longer exists inside any Venn diagram), it faces true global destruction: the system automatically performs a full “Garbage Collection”, permanently deleting the instance. (Note on Race Conditions: Because every API operation is executed within an ACID transaction using Optimistic Concurrency Control, if two concurrent operations attempt to remove an instance from its last Contexts simultaneously, the database strictly serializes the final state. The winning transaction safely performs the Garbage Collection, while the losing transaction receives a concurrency error and is rolled back).
Authorization Blast Radius in Avalanches: A common concern is whether a user triggering an avalanche in Context
C1needs explicit permissions in ContextC2if their action indirectly causes an instance to be globally Garbage Collected from ContextC2. The answer is no. An administrator operating in ContextC1only drops their own Relation instances in ContextC1. They never modify Relation instances in ContextC2. If an instance happens to lose its last remaining Context presence (which was ContextC1) and is Garbage Collected, it means by definition it was already absent from ContextC2. The blast radius of any operation is naturally, mathematically contained within the boundaries of the authorized Context.
To see this in action across multiple Contexts, consider the following scenario involving Resource instances (the exact same garbage collection mechanism applies equally to Facet and Relation instances).
1. Initial State:
Resource instance
R1is present across three Contexts:C1,C2, andC3.Resource instance
R3is present across ContextsC1andC2.Resource instance
R2exists in ContextC1.R1has outgoing IsRelatedTo instances:IRT1(withremove: cascade) pointing toR2, andIRT2(withremove: cascade) pointing toR3.
---
config:
theme: neutral
flowchart:
edgeLabelBackground: "#ffffff"
---
graph LR
R1("<b>R1</b> <br/> <sub>[<i>C1, C2, C3</i>]</sub>"):::resource
R2("<b>R2</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
R3("<b>R3</b> <br/> <sub>[<i>C1, C2</i>]</sub>"):::resource
R1 -->|"<b>IRT1</b> <br/> remove: cascade <br/> <sub>[<i>C1</i>]</sub>"| R2
R1 -->|"<b>IRT2</b> <br/> remove: cascade <br/> <sub>[<i>C1, C2</i>]</sub>"| R3
classDef resource fill:#4f81bd,stroke:#385d8a,stroke-width:3px,color:#fff;
linkStyle default stroke:#385d8a,stroke-width:3px;
2. First Remove (Remove R1 from Context C1):
A user removes R1 from Context C1. The cascade avalanche evaluates the outgoing IsRelatedTo instances in Context C1:
IRT1drops.R2is removed from ContextC1. Since ContextC1was its only Context,R2reaches a reference count of 0 and is permanently Garbage Collected.IRT2drops from ContextC1.R3is removed from ContextC1. However,R3is still present in ContextC2, so it safely survives.R1itself is removed from ContextC1. It survives because it still exists in ContextsC2andC3.
---
config:
theme: neutral
flowchart:
edgeLabelBackground: "#ffffff"
---
graph LR
R1("<b>R1</b> <br/> <sub>[<i>C2, C3</i>]</sub>"):::resource
R3("<b>R3</b> <br/> <sub>[<i>C2</i>]</sub>"):::resource
R1 -->|"<b>IRT2</b> <br/> remove: cascade <br/> <sub>[<i>C2</i>]</sub>"| R3
classDef resource fill:#4f81bd,stroke:#385d8a,stroke-width:3px,color:#fff;
linkStyle default stroke:#385d8a,stroke-width:3px;
3. Second Remove (Remove R1 from Context C2):
Later, a user removes R1 from Context C2. The avalanche evaluates the outgoing IsRelatedTo instances in Context C2:
IRT2drops from ContextC2.R3is removed from ContextC2. This was its last remaining Context.R3hits a reference count of 0 and is Garbage Collected.R1is removed from ContextC2. It safely survives because it still exists in ContextC3.
Final State:
The graph is not empty, but R2 and R3 have been physically destroyed because their respective reference counts dropped to zero. R1 is the only survivor, resting securely in its last remaining Context C3.
---
config:
theme: neutral
flowchart:
edgeLabelBackground: "#ffffff"
---
graph LR
R1("<b>R1</b> <br/> <sub>[<i>C3</i>]</sub>"):::resource
classDef resource fill:#4f81bd,stroke:#385d8a,stroke-width:3px,color:#fff;
linkStyle default stroke:#385d8a,stroke-width:3px;
Comprehensive Scenario¶
Now that the basic mechanics are clear, we can analyze a complex, real-world topology involving multiple Contexts, differing constraints, and strict schema validation. This scenario demonstrates the full power of the “avalanche effect” combining both Resource instances and multiple Facet instances.
To provide a comprehensive view, we display the Global Topology, showing all instances with their respective Contexts annotated in brackets (e.g., [C1, C2]).
Initial State¶
The scenario contains the following Resource instances:
Resource instance
R1exists in ContextsC1andC2.Resource instance
R2exists in ContextC1.Resource instance
R3exists in ContextsC1andC2.Resource instance
R4exists in ContextC2.
The scenario contains the following Facet instances:
Facet instance
F1aexists in ContextsC1andC2.Facet instance
F1bexists in ContextC1.Facet instance
F2aexists in ContextC1.Facet instance
F2bexists in ContextC1.Facet instance
F2cexists in ContextC1.Facet instance
F3aexists in ContextsC1andC2.Facet instance
F3bexists in ContextC1.Facet instance
F4aexists in ContextC2.Facet instance
F4bexists in ContextC2.
The scenario contains the following ConsistsOf instances:
R1is attached toF1avia ConsistsOf instanceCO11a, which exists in ContextsC1andC2and is configured withadd: propagateandremove: cascade.R1is attached toF1bvia ConsistsOf instanceCO11b, which exists in ContextC1and is configured withadd: propagateandremove: cascade.R2is attached toF2avia ConsistsOf instanceCO22a, which exists in ContextC1and is configured withadd: propagateandremove: cascade.R2is attached toF2bvia ConsistsOf instanceCO22b, which exists in ContextC1and is configured withadd: unpropagateandremove: cascade.R2is attached toF2cvia ConsistsOf instanceCO22c, which exists in ContextC1and is configured withadd: propagateandremove: cascade.R3is attached toF3avia ConsistsOf instanceCO33a, which exists in ContextsC1andC2and is configured withadd: propagateandremove: cascade.R3is attached toF3bvia ConsistsOf instanceCO33b, which exists in ContextC1and is configured withadd: unpropagateandremove: cascade.R4is attached toF4avia ConsistsOf instanceCO44a, which exists in ContextC2and is configured withadd: propagateandremove: cascade.R4is attached toF4bvia ConsistsOf instanceCO44b, which exists in ContextC2and is configured withadd: unpropagateandremove: cascade.
The scenario contains the following IsRelatedTo instances:
R1is linked toR2via IsRelatedTo instanceIRT12, which exists in ContextC1and is configured withadd: propagateandremove: keep.R1is linked toR3via IsRelatedTo instanceIRT13, which exists in ContextsC1andC2and is configured withadd: propagateandremove: cascadeWhenOrphan.R4is linked toR3via IsRelatedTo instanceIRT43, which exists in ContextC2and is configured withadd: unpropagateandremove: cascade.
(Note: ConsistsOf instances must implicitly use remove: cascade by definition, as a Facet instance cannot be orphaned without its parent Resource instance).
---
config:
theme: neutral
flowchart:
edgeLabelBackground: "#ffffff"
---
graph LR
R1("<b>R1</b> <br/> <sub>[<i>C1, C2</i>]</sub>"):::resource
F1a("<b>F1a</b> <br/> <sub>[<i>C1, C2</i>]</sub>"):::facet
F1b("<b>F1b</b> <br/> <sub>[<i>C1</i>]</sub>"):::facet
R2("<b>R2</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
F2a("<b>F2a</b> <br/> <sub>[<i>C1</i>]</sub>"):::facet
F2b("<b>F2b</b> <br/> <sub>[<i>C1</i>]</sub>"):::facet
F2c("<b>F2c</b> <br/> <sub>[<i>C1</i>]</sub>"):::facet
R3("<b>R3</b> <br/> <sub>[<i>C1, C2</i>]</sub>"):::resource
F3a("<b>F3a</b> <br/> <sub>[<i>C1, C2</i>]</sub>"):::facet
F3b("<b>F3b</b> <br/> <sub>[<i>C1</i>]</sub>"):::facet
R4("<b>R4</b> <br/> <sub>[<i>C2</i>]</sub>"):::resource
F4a("<b>F4a</b> <br/> <sub>[<i>C2</i>]</sub>"):::facet
F4b("<b>F4b</b> <br/> <sub>[<i>C2</i>]</sub>"):::facet
R1 -->|"<b>CO11a</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C1, C2</i>]</sub>"| F1a
R1 -->|"<b>CO11b</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C1</i>]</sub>"| F1b
R2 -->|"<b>CO22a</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C1</i>]</sub>"| F2a
R2 -->|"<b>CO22b</b> <br/> add: unpropagate <br/> remove: cascade <br/> <sub>[<i>C1</i>]</sub>"| F2b
R2 -->|"<b>CO22c</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C1</i>]</sub>"| F2c
R3 -->|"<b>CO33a</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C1, C2</i>]</sub>"| F3a
R3 -->|"<b>CO33b</b> <br/> add: unpropagate <br/> remove: cascade <br/> <sub>[<i>C1</i>]</sub>"| F3b
R4 -->|"<b>CO44a</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C2</i>]</sub>"| F4a
R4 -->|"<b>CO44b</b> <br/> add: unpropagate <br/> remove: cascade <br/> <sub>[<i>C2</i>]</sub>"| F4b
R1 -->|"<b>IRT12</b> <br/> add: propagate <br/> remove: keep <br/> <sub>[<i>C1</i>]</sub>"| R2
R1 -->|"<b>IRT13</b> <br/> add: propagate <br/> remove: cascadeWhenOrphan <br/> <sub>[<i>C1, C2</i>]</sub>"| R3
R4 -->|"<b>IRT43</b> <br/> add: unpropagate <br/> remove: cascade <br/> <sub>[<i>C2</i>]</sub>"| R3
classDef resource fill:#4f81bd,stroke:#385d8a,stroke-width:3px,color:#fff;
linkStyle default stroke:#385d8a,stroke-width:3px;
classDef facet fill:#9bbb59,stroke:#4f6228,stroke-width:3px,color:#fff;
Scenario A: The Add Avalanche¶
If a manager acting from Context C2 (starting from the Initial State (Pre-Add)) adds R1 into a new Context C3, the avalanche evaluates only the outgoing Relation instances visible from R1 in Context C2.
F1b, R2 (and its Facet instances) are completely invisible to this operation.
For
R1:F1ais propagated.R3is propagated.For
R3(pulled viaR1): Its outgoing ConsistsOf instances in ContextC2are evaluated.F3ais pushed to ContextC3(propagate).
The updated Global Topology shows [C3] added only to R1, F1a, R3, and F3a, and to the IsRelatedTo and ConsistsOf instances connecting them (CO11a, IRT13, and CO33a):
---
config:
theme: neutral
flowchart:
edgeLabelBackground: "#ffffff"
---
graph LR
R1("<b>R1</b> <br/> <sub>[<i>C1, C2, C3</i>]</sub>"):::resource
F1a("<b>F1a</b> <br/> <sub>[<i>C1, C2, C3</i>]</sub>"):::facet
F1b("<b>F1b</b> <br/> <sub>[<i>C1</i>]</sub>"):::facet
R2("<b>R2</b> <br/> <sub>[<i>C1</i>]</sub>"):::resource
F2a("<b>F2a</b> <br/> <sub>[<i>C1</i>]</sub>"):::facet
F2b("<b>F2b</b> <br/> <sub>[<i>C1</i>]</sub>"):::facet
F2c("<b>F2c</b> <br/> <sub>[<i>C1</i>]</sub>"):::facet
R3("<b>R3</b> <br/> <sub>[<i>C1, C2, C3</i>]</sub>"):::resource
F3a("<b>F3a</b> <br/> <sub>[<i>C1, C2, C3</i>]</sub>"):::facet
F3b("<b>F3b</b> <br/> <sub>[<i>C1</i>]</sub>"):::facet
R4("<b>R4</b> <br/> <sub>[<i>C2</i>]</sub>"):::resource
F4a("<b>F4a</b> <br/> <sub>[<i>C2</i>]</sub>"):::facet
F4b("<b>F4b</b> <br/> <sub>[<i>C2</i>]</sub>"):::facet
R1 -->|"<b>CO11a</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C1, C2, C3</i>]</sub>"| F1a
R1 -->|"<b>CO11b</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C1</i>]</sub>"| F1b
R2 -->|"<b>CO22a</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C1</i>]</sub>"| F2a
R2 -->|"<b>CO22b</b> <br/> add: unpropagate <br/> remove: cascade <br/> <sub>[<i>C1</i>]</sub>"| F2b
R2 -->|"<b>CO22c</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C1</i>]</sub>"| F2c
R3 -->|"<b>CO33a</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C1, C2, C3</i>]</sub>"| F3a
R3 -->|"<b>CO33b</b> <br/> add: unpropagate <br/> remove: cascade <br/> <sub>[<i>C1</i>]</sub>"| F3b
R4 -->|"<b>CO44a</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C2</i>]</sub>"| F4a
R4 -->|"<b>CO44b</b> <br/> add: unpropagate <br/> remove: cascade <br/> <sub>[<i>C2</i>]</sub>"| F4b
R1 -->|"<b>IRT12</b> <br/> add: propagate <br/> remove: keep <br/> <sub>[<i>C1</i>]</sub>"| R2
R1 -->|"<b>IRT13</b> <br/> add: propagate <br/> remove: cascadeWhenOrphan <br/> <sub>[<i>C1, C2, C3</i>]</sub>"| R3
R4 -->|"<b>IRT43</b> <br/> add: unpropagate <br/> remove: cascade <br/> <sub>[<i>C2</i>]</sub>"| R3
classDef resource fill:#4f81bd,stroke:#385d8a,stroke-width:3px,color:#fff;
linkStyle default stroke:#385d8a,stroke-width:3px;
classDef facet fill:#9bbb59,stroke:#4f6228,stroke-width:3px,color:#fff;
Scenario B: Alternate Source Context¶
If, instead of starting from Context C2, the exact same add operation originates from Context C1 (starting from the Initial State (Pre-Add)), the avalanche evaluates the outgoing Relation instances visible only from Context C1:
For
R1: BothF1aandF1bare pushed to ContextC3(propagate).R2andR3are also pushed to ContextC3(propagate).For
R2(pulled viaR1): Its outgoing ConsistsOf instances in ContextC1are evaluated.F2aandF2care pushed to ContextC3(propagate), whileF2bis left behind (unpropagate).For
R3(pulled viaR1): Its outgoing ConsistsOf instances in ContextC1are evaluated.F3ais pushed to ContextC3(propagate), whileF3bis left behind (unpropagate).R4and its Facet instances are entirely invisible from ContextC1and are unaffected.
The updated Global Topology shows [C3] added to the propagated elements:
---
config:
theme: neutral
flowchart:
edgeLabelBackground: "#ffffff"
---
graph LR
R1("<b>R1</b> <br/> <sub>[<i>C1, C2, C3</i>]</sub>"):::resource
F1a("<b>F1a</b> <br/> <sub>[<i>C1, C2, C3</i>]</sub>"):::facet
F1b("<b>F1b</b> <br/> <sub>[<i>C1, C3</i>]</sub>"):::facet
R2("<b>R2</b> <br/> <sub>[<i>C1, C3</i>]</sub>"):::resource
F2a("<b>F2a</b> <br/> <sub>[<i>C1, C3</i>]</sub>"):::facet
F2b("<b>F2b</b> <br/> <sub>[<i>C1</i>]</sub>"):::facet
F2c("<b>F2c</b> <br/> <sub>[<i>C1, C3</i>]</sub>"):::facet
R3("<b>R3</b> <br/> <sub>[<i>C1, C2, C3</i>]</sub>"):::resource
F3a("<b>F3a</b> <br/> <sub>[<i>C1, C2, C3</i>]</sub>"):::facet
F3b("<b>F3b</b> <br/> <sub>[<i>C1</i>]</sub>"):::facet
R4("<b>R4</b> <br/> <sub>[<i>C2</i>]</sub>"):::resource
F4a("<b>F4a</b> <br/> <sub>[<i>C2</i>]</sub>"):::facet
F4b("<b>F4b</b> <br/> <sub>[<i>C2</i>]</sub>"):::facet
R1 -->|"<b>CO11a</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C1, C2, C3</i>]</sub>"| F1a
R1 -->|"<b>CO11b</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C1, C3</i>]</sub>"| F1b
R2 -->|"<b>CO22a</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C1, C3</i>]</sub>"| F2a
R2 -->|"<b>CO22b</b> <br/> add: unpropagate <br/> remove: cascade <br/> <sub>[<i>C1</i>]</sub>"| F2b
R2 -->|"<b>CO22c</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C1, C3</i>]</sub>"| F2c
R3 -->|"<b>CO33a</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C1, C2, C3</i>]</sub>"| F3a
R3 -->|"<b>CO33b</b> <br/> add: unpropagate <br/> remove: cascade <br/> <sub>[<i>C1</i>]</sub>"| F3b
R4 -->|"<b>CO44a</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C2</i>]</sub>"| F4a
R4 -->|"<b>CO44b</b> <br/> add: unpropagate <br/> remove: cascade <br/> <sub>[<i>C2</i>]</sub>"| F4b
R1 -->|"<b>IRT12</b> <br/> add: propagate <br/> remove: keep <br/> <sub>[<i>C1, C3</i>]</sub>"| R2
R1 -->|"<b>IRT13</b> <br/> add: propagate <br/> remove: cascadeWhenOrphan <br/> <sub>[<i>C1, C2, C3</i>]</sub>"| R3
R4 -->|"<b>IRT43</b> <br/> add: unpropagate <br/> remove: cascade <br/> <sub>[<i>C2</i>]</sub>"| R3
classDef resource fill:#4f81bd,stroke:#385d8a,stroke-width:3px,color:#fff;
linkStyle default stroke:#385d8a,stroke-width:3px;
classDef facet fill:#9bbb59,stroke:#4f6228,stroke-width:3px,color:#fff;
Scenario C: The Remove Avalanche¶
Starting directly from the state established in Scenario B: Adding R1 from Context C1 to Context C3, the manager now attempts to remove R1 from Context C1. The avalanche evaluates the remove constraints on all outgoing Relation instances visible in Context C1:
For
R1: It is removed from ContextC1. Its outgoing ConsistsOf instancesCO11aandCO11bdrop (cascade):F1ais removed from ContextC1but survives in ContextsC2, C3.F1bis removed from ContextC1. Since it existed in ContextsC1andC3, its Context presence count drops to 1, so it safely remains in[C3]and is not Garbage Collected. (Note: if it had been exclusively in ContextC1, its count would have hit zero and it would be Garbage Collected).
On
R2: The IsRelatedTo instanceIRT12drops, but its constraint iskeep.R2safely survives in ContextC1(retaining all its Facet instances in ContextC1).On
R3: The IsRelatedTo instanceIRT13drops, and its constraint iscascadeWhenOrphan.R3checks for other incoming IsRelatedTo instances in ContextC1. Finding none, it is removed from ContextC1.Because
R3is removed from ContextC1, its Facet instances must also follow thecascade:F3asurvives in ContextsC2, C3, butF3bdrops to zero Contexts and is permanently Garbage Collected.
The final Global Topology perfectly illustrates how keep, cascadeWhenOrphan, and Facet instance Garbage Collection shape the graph through cascading side effects:
---
config:
theme: neutral
flowchart:
edgeLabelBackground: "#ffffff"
---
graph LR
R1("<b>R1</b> <br/> <sub>[<i>C2, C3</i>]</sub>"):::resource
F1a("<b>F1a</b> <br/> <sub>[<i>C2, C3</i>]</sub>"):::facet
F1b("<b>F1b</b> <br/> <sub>[<i>C3</i>]</sub>"):::facet
R2("<b>R2</b> <br/> <sub>[<i>C1, C3</i>]</sub>"):::resource
F2a("<b>F2a</b> <br/> <sub>[<i>C1, C3</i>]</sub>"):::facet
F2b("<b>F2b</b> <br/> <sub>[<i>C1</i>]</sub>"):::facet
F2c("<b>F2c</b> <br/> <sub>[<i>C1, C3</i>]</sub>"):::facet
R3("<b>R3</b> <br/> <sub>[<i>C2, C3</i>]</sub>"):::resource
F3a("<b>F3a</b> <br/> <sub>[<i>C2, C3</i>]</sub>"):::facet
R4("<b>R4</b> <br/> <sub>[<i>C2</i>]</sub>"):::resource
F4a("<b>F4a</b> <br/> <sub>[<i>C2</i>]</sub>"):::facet
F4b("<b>F4b</b> <br/> <sub>[<i>C2</i>]</sub>"):::facet
R1 -->|"<b>CO11a</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C2, C3</i>]</sub>"| F1a
R1 -->|"<b>CO11b</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C3</i>]</sub>"| F1b
R2 -->|"<b>CO22a</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C1, C3</i>]</sub>"| F2a
R2 -->|"<b>CO22b</b> <br/> add: unpropagate <br/> remove: cascade <br/> <sub>[<i>C1</i>]</sub>"| F2b
R2 -->|"<b>CO22c</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C1, C3</i>]</sub>"| F2c
R3 -->|"<b>CO33a</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C2, C3</i>]</sub>"| F3a
R4 -->|"<b>CO44a</b> <br/> add: propagate <br/> remove: cascade <br/> <sub>[<i>C2</i>]</sub>"| F4a
R4 -->|"<b>CO44b</b> <br/> add: unpropagate <br/> remove: cascade <br/> <sub>[<i>C2</i>]</sub>"| F4b
R1 -->|"<b>IRT12</b> <br/> add: propagate <br/> remove: keep <br/> <sub>[<i>C3</i>]</sub>"| R2
R1 -->|"<b>IRT13</b> <br/> add: propagate <br/> remove: cascadeWhenOrphan <br/> <sub>[<i>C2, C3</i>]</sub>"| R3
R4 -->|"<b>IRT43</b> <br/> add: unpropagate <br/> remove: cascade <br/> <sub>[<i>C2</i>]</sub>"| R3
classDef resource fill:#4f81bd,stroke:#385d8a,stroke-width:3px,color:#fff;
linkStyle default stroke:#385d8a,stroke-width:3px;
classDef facet fill:#9bbb59,stroke:#4f6228,stroke-width:3px,color:#fff;
Advanced Scenarios: The operations detailed above (Scenarios A, B, and C) cover standard tree-like topologies. The model’s behavior becomes significantly more complex when dealing with topological cycles (loops) and diamond patterns (especially in conjunction with
cascadeWhenOrphan). To explore how the system guarantees cycle-breaking and safe Garbage Collection in these advanced topologies, refer to Complex Topology Scenarios.(Note: The complex scenarios documented in the appendix are self-contained and fully understandable without prior knowledge of the REST APIs.)