Hi team,
in order to finish implementation of Create(Root)NodeAggregate and CreateNodeVariant, I need a decision on how to handle tethered nodes. Beforehand, a few definitions:
- Tethered node (fka auto created child node): A node that is coupled to its parent as a container for further properties and child nodes. Most prominent example: The main content collection
- Specialization node variant: A variant of a node in a specialized dimension space point compared to the source node’s DSP
- Generalization node variant: A variant of a node in a generalized dimension space point compared to the source DSP
- Peer node variant: A variant of a node in a dimension space point that is neither a specialization nor a generalization compared to the source DSP
Suggestion:
Tethering works on node aggregate / global level. That means that all variants of a tethered node act to structural changes simultaneously. But in addition to that, they cannot be directly structurally changed, this only works on a global scale per node (aggregate) type. This has the following consequences on the command side:
-
CreateNodeAggregateWithNode will always create all tethered child nodes, albeit in separate events. The command handler must prevent direct creation of nodes on paths reserved by the parent’s node type for tethered children
-
CreateNodeVariant will always create variants of the tethered children alongside the parent. The command handler must prevent direct variation of tethered nodes.
We need this at least for creating peer variants since their children cannot use fallbacks. In consequence we need this behaviour for all variation to keep things consistent:
Given DSPs A, A’ and B with A’ specialization of A and B peer to the others. When creating a peer variant from B to A, then B to A’ all tethered children would be created, when creating a peer variant from B to A and then specializing from A to A’ using the available fallbacks, this would not be the case although the result should be identical. Thus: Always create all variants for tethered children. -
DeleteNodeVariant must always remove all variants in the same origin DSP of all tethered children, regardless whether they have valid fallback parents. The command handler must prevent direct deletion of tethered node variants
-
DeleteNodeAggregate will always remove all tethered child aggregates, most probably the same way it does with regular node aggregates. The command handler must prevent direct deletion of tethered node aggregates
-
ChangeNodeAggregateType (RetypeNodeAggregate?) must be prevented by the command handler when trying to be applied on a tethered child node aggregate. This can only be changed globally with a RetypeTetheredNodeAggregates command which affects all tethered node aggregates beneath parent node aggregates of a given type along a given name matching the new NodeType config
-
RenameNodeAggregate must be prevented by the command handler when trying to be applied on a tethered child node aggregate. This can only be changed globally with a RenameTetheredNodeAggregates command which affects all tethered node aggregates beneath parent node aggregates of a given type along a given name matching the new NodeType config
-
HideNode and ShowNode might work on node level if allowed at all for tethered children
-
MoveNode must be prevented by the command handler when trying to be applied on a tethered child node aggregate.
-
SetProperties and SetReferences should work on node level
To accomplish this, we need to be able to distinguish between regular and tethered nodes. We can do so by either defining separate events for them (TetheredNodeAggregateWithNodeWasCreated) or extending the current ones by a flag (tethered: bool) so that the write side node aggregate can enforce hard constraints if necessary and the projectors can evaluate this information to allow for soft constraint checks and UI support.
In consequence I think we can handle root nodes similarly (root: bool) or with a shared multivalue flag (scope: regular/root/tethered) to be independent from the current node type config.
What do you think?