Skip to content
This repository has been archived by the owner on May 3, 2022. It is now read-only.

Work in Progress on the Event Sourced Content Repository as installable set of packages

Notifications You must be signed in to change notification settings


Repository files navigation

Event Sourced Content Repository Collection

Build Status StyleCI

This is the package bundle you can install alongside a plain Neos to play around with the event-sourced CR.

Feature comparison

βœ… Done

⏩ Currently worked on

🚫 Will not be supported

Feature Current CR Event Sourced CR
Create/ Edit / Delete Nodes βœ… βœ…
Shortcut Handling βœ… βœ…
Query Nodes βœ… βœ…
Cut / Copy / Paste βœ… βœ…
Move Nodes βœ… βœ…
Hide Nodes βœ… βœ…
History (βœ…)
Basic Workspaces βœ… βœ…
Workspace Module βœ… βœ…
Nested Workspaces βœ…
Undo / Redo 🚫
Setting Start / End time βœ…
Resolving Referencing Nodes 🚫 βœ…
Menu Rendering βœ… βœ…
Dimension Menu Rendering βœ… βœ…
Supporting "not in menu" βœ… βœ…
Change node type βœ… βœ…
Dimensions βœ… βœ…
Dimension Fallback βœ… βœ…
Multiple Sites βœ… βœ…
Permissions / Policy βœ…
Export / Import βœ… βœ…β©
Node Migrations βœ… βœ…
Structure Adjustments a.k.a. node:repair βœ… βœ…
Integrity Checks 🚫 βœ…
Separate Read and Write API 🚫 βœ…
More convenient write API
Extensible Read API (βœ…) custom Node βœ… NodeAccessors
FlowQuery is compatible βœ… βœ…
Advanced test cases 🚫 βœ…
Don't use ORM, but direct SQL queries 🚫 βœ…
Asynchronous operations possible 🚫 βœ…
performant node moving 🚫 βœ…
performant node deletion 🚫 βœ…
near-constant read performance 🚫 βœ…
performant URL generation (routing) 🚫 βœ…
MySQL support βœ… βœ…
Postgres support βœ… ⏩ (much higher performance)
usage without Neos/Flow 🚫 prepared
extensible property serialization 🚫 βœ… through Symfony Serializer
traverse node references in both directions 🚫 βœ…
content merge conflict detection 🚫 βœ…
content merge conflict resolution 🚫 ⏩
User Interface
Ensure node deletion can be published in UI βœ… βœ…
Support Dimension Constraints βœ…
Publish Workspace βœ… βœ…
Publish Current Page βœ… βœ…
Discard all βœ… βœ…
Discard Current Page βœ… βœ…
Change node type in UI βœ… βœ…

Package Compatibility

  • Flowpack.NodeTemplates is currently NOT supported because it works heavily with stateful Node objects. In principle, it can be possible to build an API-compatible version based on the ES CR, by providing a "Node" shim object.



The Event Sourced Content Repository relies on a feature called (Recursive) Common Table Expressions (CTE) that require

Lateron, we will also support PostgreSQL. (We know it will work, but we did not create migrations or did testing yet).


The new code should be compatible with PHP 7.4

Getting Started / Installation



Linting is done via CodeSniffer and PHPStan. Both are integrated as composer scripts (see composer.json). To manually lint your branch before opening a PR, you can run

composer lint

from the folder the collection resides (probably Packages/CR in a Neos distribution)

Commit hooks

Linting can be added to commit hooks if desired. In the collection folder (probably Packages/CR in a Neos distribution), add lines similar to the following to your .git/hooks/pre-commit file (the example is for a DDEV environment):

ddev exec "cd Packages/CR; composer lint"

Road to first running beta

  • create standalone package collection instead of branches
  • command to import from NodeData to events
  • make it work with Neos.ContentRepository.DimensionSpace package
  • ensure Behavioral Tests run again
  • Update CR for Neos 5
  • Update EventSourcedNeosAdjustments for Neos 5
  • Content Cache (#103)
  • ensure Functional Tests run again
  • figure out how/if to use event sourced Site/Domain (!possibly difficult!) -> fixed; won't use event sourced site/domain
  • change RoutePart Handler when using event-sourced mode
  • adjust NodeController when using event-sourced mode
  • add switch to use event-sourced read model in Fusion rendering (!possibly difficult due to method signatures!)
  • allow to open User Interface based on Event-Sourced read model
  • implement Show/Hide of nodes (recursively)
  • create Commands in response to UI interaction
    • SetProperty command
    • CreateNode
    • MoveNode
    • ShowNode
    • DisableNode
  • create feature list in this README, detailing what is supported and what not yet.
  • support empty content dimension values in URL; e.g. "/de/..." for german, and "/..." for english
  • absolute node referencing for ContentCollection (e.g. shared footer in Demo Site)
  • fix Policy handling to configure permissions for various UI parts
  • fix structure tree
  • show correct workspace state after reload (top publish button)
  • [?] fix inline linking
  • fix node tree search
  • [?] fix node tree filter
  • Implement Node Repair
  • (further TODOs here; this list is not complete yet)

Development of the Postgres Adapter

By default, the Mysql Adapter is active right now, as Postgres is still in development.

To activate Postgres, right now, the following steps are needed in your distribution:

# Configuration/Objects.yaml

  className: 'Neos\ContentGraph\PostgreSQLAdapter\Domain\Repository\ContentHypergraph'

if you want to run Postgres and MySQL side by side for the tests, you need the following config:

# Configuration/Settings.yaml

          'Neos\ContentGraph\PostgreSQLAdapter\Domain\Projection\HypergraphProjector': true
          'Postgres': 'Neos\ContentGraph\PostgreSQLAdapter\Domain\Repository\ContentHypergraph'
          'Neos\ContentGraph\PostgreSQLAdapter\Domain\Projection\HypergraphProjector': true

if you want to run Postgres without MySQL, you need the following config:

# Configuration/Settings.yaml

          'Neos\ContentGraph\PostgreSQLAdapter\Domain\Projection\HypergraphProjector': true
          'Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\GraphProjector': false
          'Postgres': 'Neos\ContentGraph\PostgreSQLAdapter\Domain\Repository\ContentHypergraph'
          'DoctrineDBAL': false
          'Neos\ContentGraph\PostgreSQLAdapter\Domain\Projection\HypergraphProjector': true
          'Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\GraphProjector': false

Technical Description (for developers)

This section should give an overview about the different involved packages, to ease understanding the different moving parts.


see neos/neos-development-collection#2202 for the Pull Request.

  • in namespace Domain\Projection\Content, the new NodeInterface and TraversableNodeInterface are defined.
  • in namespace Domain\ValueObject, corresponding value objects are defined.
  • the old Neos\ContentRepository\Domain\Model\Node implements the full new NodeInterface and most of TraversableNodeInterface. This is needed to ensure we can build FlowQuery implementations which can work with old and new API at once.
  • adjusted FlowQuery operations to TraversableNodeInterface (TODO not yet all of them)


see neos/neos-development-collection#2202 for the Pull Request.

  • various detail improvements to use TraversableNodeInterface in the core (e.g. FusionView)


APIs to query the configured dimension space

CR / Neos.EventSourcedContentRepository

Transition package implementing the event sourced CR core. In the longer run, will probably be merged into Neos.ContentRepository.

  • Domain\Context\* implements Commands, Command Handlers, Events for Workspace, Content Stream, Nodes
  • Domain\Projection\* implements projections for changes, workspace listing; and contains the definition for the main Content Graph projection (ContentGraphInterface and ContentSubgraphInterface)

CR / Neos.ContentGraph.DoctrineDbalAdapter

implementation of the ContentGraphInterface and ContentSubgraphInterface using MySQL queries.

CR / Neos.ContentGraph.PostgreSQLAdapter

implementation of the ContentGraphInterface and ContentSubgraphInterface using PostgreSQL queries.

CR / Neos.EventSourcedNeosAdjustments

It turns out that there are numerous changes needed to the details of Neos.Neos - so this package hooks into various places in the Neos lifecycle to override certain Neos functionality.

We often completely override certain classes / behaviors from the Neos core completely; so that should make merging the changes back to the Neos.Neos package at some point a lot easier because we can then replace full classes instead of only individual pieces.

This package consists of the following bounded contexts, listed in their order during request processing:


This contains a CommandController and a service to generate events from reading NodeData. It can be activated using the new CLI command.


We replace the default FrontendNodeRoutePartHandler by providing an extra implementation of FrontendNodeRoutePartHandlerInterface.

Activation: We replace the implementation of FrontendNodeRoutePartHandlerInterface in Objects.yaml.

  • internally, the Http and Routing namespaces are used for behaviours internal to the routing.


This is a replacement for Frontend\NodeController of Neos.Neos.

Activation: We trigger this controller by AOP (in NodeControllerAspect): We call the new controller when processRequest() is called for the Neos controller.


  • We replace certain Fusion implementations which are already re-implemented to work more efficiently with the ContentGraph API; and which implement linking (because this API also changed). This includes:

    • Menu / DimensionMenu
    • NodeUri, ConvertUris
    • ContentElementEditable / ContentElementWrapping (because the ContentElementWrapping service has changed quite a lot)
    • Activation: using fusion autoInclude in Settings.yaml, we load the Fusion file resource://Neos.EventSourcedNeosAdjustments/Private/Fusion/Root.fusion. This Root.fusion replaces the implementations for the aforementioned Fusion objects, so things work as expected for integrators (without new namespaces).
  • Eel NodeHelper and WorkspaceHelper

    • Activation: These helpers are registered under the names Neos.EventSourcedNeosAdjustments.*; so a separate name. These helpers are explicitely used in the Root.fusion mentioned a few lines above.
  • custom ExceptionHandler because this also needs the replacement ContentElementWrappingService.

    • Activation: This helper is used as exception handlers in the Root.fusion mentioned a few lines above.
    • If people used these exception handlers themselves, they need to reconfigure them to the new implementations.


  • We replace Linking and Content Element Wrapping ViewHelpers, because Node Linking has changed and ContentElementWrapping has changed as well.
    • Activation: Using AOP, the ViewHelperReplacementAspect implements aliasing of ViewHelper classes; effectively returning the VHs in this namespace instead of the default ones.


We implement a completely new ContentElementWrappingService and ContentElementWrappingService; mainly because they change quite a bit and their interfaces now require TraversableNodeInterface instead of the legacy NodeInterface.

The new services are used in the overridden ViewHelpers (see section Fluid above); and in overridden Fusion implementations (see section Fusion above).

NodeAddress (Domain\Context\Content)

A NodeAddress is an external representation of a node (used in routing). TODO: Move to Neos.EventSourcedContentRepository.


  • BackendController is an alternative implementation for Neos.Neos.Ui BackendController.
    • Activation: We trigger this controller by AOP (in BackendControllerAspect): We call the new controller when processRequest() is called for the Neos backend controller.
  • We create Content Streams on backend login using the EditorContentStreamZookeeper (TODO change name maybe?).
    • Activation: We trigger this service by Signal/Slot in Package.php.
  • Fusion (for backend)
    • Activation: We load a custom resource://Neos.EventSourcedNeosAdjustments/Private/Fusion/Backend/Root.fusion using Views.yaml.
    • custom NodeInfoHelper, calling to a custom NodePropertyConverterService
  • adjust the DimensionSwitcher JS component in Resources/Private/UiAdapter
  • TODO: this is not everything yet.


Work in Progress on the Event Sourced Content Repository as installable set of packages


Code of conduct

Security policy




Sponsor this project