RFC: Tethered Nodes (Event Sourced CR)

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?

Does‘nt that require the type of a node beeing also a value that is the same across all DSP as you suggest for the tethered nodes?

Yes it does.Things with the same external identity should also be of the same type.

That is consequent and probably also a must for the idea of aggregate properties in future.

Nonetheless it is an important conceptual change especially since editors might change the type without noticing that they are manipulating all dimensions at once.

Agreed, this must be made clear to the user via UI interaction, especially since changing a node aggregate’s type is one of the more complex operations available.

Hey Bernhard,

totally agree to what you wrote and suggested.

I think such a multivalue flag is a good idea :slight_smile:

All the best,
Sebastian

As just mentioned in our sync meeting, the suggestion makes a lot of sense to me conceptually!

I still struggle a bit with the term “tethered node” and would prefer something simpler like “attached node” or so. But that might well be just a personal preference.

The other little detail I stumbled upon is “multivalue flag”. IMO that’s always a bit harder to use and more error prone as it leads to code like:

${node.scope == 'tethered' ? 'foo' : 'bar'}

While sth. like

${node.isTethered ? 'foo' : 'bar'}

is cleaner IMO - even if it means that we have two boolean flags.


(I know, this is not about Fusion per se, but it’s the same in PHP code IMO)