Event Sourced CR - Technical TODO list

Hey everybody,

(this post probably only makes sense for people involved in the project)

I’ve started to work at https://github.com/skurfuerst/contentrepository-development-collection (which we should move to the Neos namespace now I’d say). Status is as follows:

  • classes compile for the first time.
  • NodeData -> Events works
  • Readme added
  • code is based on Neos.ContentRepository.DimensionSpace now
  • Routing based on the event-sourced code works (with feature toggle to switch old/new behavior)
  • Frontend\NodeController based on the event sourced code works (with feature toggle to switch old/new behavior)

Now, the most difficult part is to ensure Fusion etc renders properly. This is difficult because 1) some Fusion prototypes should be replaced altogether (easier) and 2) the current codebase relies heavily on the legacy NodeInterface (harder).

Mapping from old NodeInterface to new TraversableNodeInterface

// old NodeInterface -> new NodeInterface
-> getSubgraph(): ContentSubgraphInterface;
-> getContentStreamIdentifier(): ContentStreamIdentifier;
-> getNodeIdentifier(): NodeIdentifier;
getIdentifier(); (string) ->  getNodeAggregateIdentifier(): NodeAggregateIdentifier;
-> getNodeTypeName(): NodeTypeName;
getNodeType() -> getNodeType(): NodeType;
getName(); (string) -> getNodeName(): NodeName;
-> getDimensionSpacePoint(): DimensionSpacePoint;
!! getProperties(); (array) -> getProperties(): PropertyCollection;
getProperty($propertyName); (mixed) -> getProperty($propertyName);
hasProperty($propertyName); (bool) -> hasProperty($propertyName): bool;
isHidden(); (boolean) -> isHidden() : boolean;
getLabel(); (string) -> getLabel(): string;

// old NodeInterface -> new TraversableNodeInterface
-> getContextParameters(): Domain\Context\Parameters\ContextParameters;
getParent(); (NodeInterface) -> getParent(): ?TraversableNodeInterface;  -> findParentNode()
getPath(); (string) -> getNodePath(): NodePath; -> findNodePath()
!! only one level deep getNode($path); (NodeIface) -> findNamedChildNode(NodeName $nodeName): ?TraversableNodeInterface;
 getChildNodes($nodeTypeFilter = null, $limit = null, $offset = null); array<\Neos\ContentRepository\Domain\Model\NodeInterface>
 -> getChildNodes(NodeTypeConstraints $nodeTypeConstraints = null, $limit = null, $offset = null); -> findChildNodes()
 getContextPath(); (string) -> getNodeAddress (NodeAddress)

we’d have to check the following API methods from old NodeInterface whether we need to support them at all:

(we check this off once we checked whether we need to support it; with a short description afterwards)

- [x] getPropertyNames(); (array)
    -> not used at all outside of "Node"
- [x] getWorkspace(); (Workspace)
    -> used in ContentElementWrappingService; that is something we might need to override.
- [x] getPrimaryChildNode(); (NodeInterface)
    -> unused at all
- [x] hasChildNodes($nodeTypeFilter = null); bool
    -> only used in HtmlAugumenter, NodeTreeBuilder and NodeView
- [x] isNodeTypeAllowedAsChildNode(NodeType $nodeType); bool
    -> only used in Create/Copy/Move Commands -> will be moved to Command Handler.
- [x] getDimensions(); array
    -> only in Node::createRecursiveCopy and NodeContentDimensionsInformation.html
- [ ] !!!! isAutoCreated(); boolean
    -> that is a more difficult one; unsure how to solve this one.
- [x] getOtherNodeVariants(); array<NodeInterface>
    -> unused!
- [x] getContentObject(); ...
    -> unused!
- [ ] getHiddenBeforeDateTime(); (DateTime)
- [ ] getHiddenAfterDateTime(); (DateTime)
- [ ] isHiddenInIndex(); (boolean)
- [x] getNodeData(); (NodeData)
    -> not used during rendering; except "ContentChangeDiff.html"
- [ ] isRemoved(); bool
- [ ] getAccessRoles(); (array)
- [ ] hasAccessRestrictions(); bool
- [ ] isVisible(); bool
- [ ] isAccessible(); bool
- [x] getDepth(); (int)
    -> might be used in Menu fusion; but we replace Menu anyways completely.
- [x] getIndex(); (int)
   -> not used
- [x] getParentPath(); (string)
   -> not really used I'd say
- !!! [ ] getContext(); (Context)
   -> that's a difficult one we need to check case by case.

We do not support the write side of NodeInterface (just included for clarity)

// NodeInterface WRITE
setProperty($propertyName, $value);
setNodeType(NodeType $nodeType);
setHiddenBeforeDateTime(\DateTime $dateTime = null);
setWorkspace(Workspace $workspace);
createNode($name, NodeType $nodeType = null, $identifier = null);
createSingleNode($name, NodeType $nodeType = null, $identifier = null);
createNodeFromTemplate(NodeTemplate $nodeTemplate, $nodeName = null);
moveBefore(NodeInterface $referenceNode);
moveAfter(NodeInterface $referenceNode);
moveInto(NodeInterface $referenceNode);
copyBefore(NodeInterface $referenceNode, $nodeName);
copyAfter(NodeInterface $referenceNode, $nodeName);
copyInto(NodeInterface $referenceNode, $nodeName);
createVariantForContext($context); NodeInterface
setHiddenAfterDateTime(\DateTime $dateTime = null);
setAccessRoles(array $accessRoles);

I am unsure whether we should pursure a stand-alone CR library nowish

Hey everybody,

we once had the idea to make the CR a library which can be used standalone, outside of Neos/Flow.

I think this would generally be feasible, when we do the following:

  • in the Event Sourcing package, use Symfony Serializer instead of our Property Mapper (I think this would be really good)
  • make the event sourcing core package a library; e.g. explicitely registering event names etc
  • only use Constructor injection instead of @Inject annotations in the library.

It might be more difficult to split it all apart as soon as the code base grows; compared to doing it now. On the other hand, it will result in a more complex package setup; and it kind of side-tracks us from creating business value now:

  • we’d need a new library containing TraversableNodeInterface, NodeInterface and related objects (which the current CR can depend on)
  • we’d have to split the EventSourcedContentRepository into a library and a connector.

What do you think about this @bwaidelich @daniellienert @christianm and others? :slight_smile:

All the best, Sebastian

Hey @sebastian,

I absolutely don’t grasp was that means for the project in detail in terms of complexity and effort.

In general I would avoid side goals that makes a such complex and big task bigger and more complex.
Building a package that can be used independently and for something else then our use case also makes it difficult to do any shortcut towards our own use case.
If we get that thing running and if it gets a success, we’ll know how we would build it right - I would rather make that second version a standalone package.

Im absolutely fine if we go the other way. Its just my experience with solving a complex domain specific task by building a general-purpose solution. :relaxed:


I would not split the package yet but prepare for a later split with reasonable effort.

A good starting point could be to have separate „Neos Adapter“ and „Core“ namespaces inside the cr-package where dependency injection and other Flow magic is used only on the NeosAdapter side.

Offcourse there will still be issues when the packages are split but those can be worked out later.

1 Like

Is there a use case where a generic Event Sourced Content Repository would be used and payed by the client? I‘m not sure if it‘s worth the effort to add another level of abstraction (CR core and Neos adapter).

Maybe for a later version after we shipped it as default.

(Just reminding that Flow was created before Neos and is basically a framework that has no marketshare.)

Take the easy, low-hanging fruits now (replace serializer, constructor injection) and leave the rest to the point where it really makes sense to have it fully independent.

1 Like