Skip to content
CAI
Software that uses CAICheck a score

neos/Neos.EventSourcing

51.9

Adequate · 21 September 2026

4.7k

lines of production code

PHP

primary language

4

measurements over time

CAI band scale
CAI trend line
CAI lens gauges

What this system is

This system is an event sourcing infrastructure library that manages domain state by recording, storing, and replaying immutable domain events. It provides core components for aggregate root management, event storage with concurrency control, and asynchronous projection updates via a job queue. The system facilitates the reconstruction of application state from event streams and supports multiple storage backends, primarily through a Doctrine-based implementation.

How it got here

2016 — CQRS to EventSourcing migration

14 changes.

The legacy CQRS package was removed and replaced by the new Neos.EventSourcing package, introducing a redesigned EventStore with Doctrine backend support and pluggable storage adapters. This period established core infrastructure for event sourcing, including projection management, replay capabilities, and lifecycle hooks for event listeners.

2018–2019 — Event Sourcing Infrastructure

9 changes.

This period focused on implementing the core event sourcing infrastructure, including the Aggregate Root base class, event recording interfaces, and specialized exception handling. It also introduced robust event normalization for value objects and proxy classes, alongside concurrency control mechanisms for event listeners to ensure safe state tracking.

2020 — Event sourcing infrastructure and testing

9 changes.

This period focused on implementing the core event publishing and listener mapping subsystems, introducing asynchronous catch-up processing via job queues to improve performance. Comprehensive unit tests were added to validate the new event publisher, listener invoker, and mapping providers, ensuring robust configuration handling and delivery guarantees.

Features

Add InMemoryStreamIterator for in-memory event stream iteration

A new InMemoryStreamIterator class has been introduced to support iterating over event streams stored in memory, primarily for testing purposes. This implementation adheres to the EventStreamIteratorInterface, allowing consumers to traverse raw events with proper handling of payloads, metadata, and timestamps within an in-memory context.

Classes/EventStore/Storage/InMemory · high confidence

Asynchronous catch-up processing for event listeners

The system now supports asynchronous processing of event listeners via a new \CatchUpEventListenerJob\. This job, part of the JobQueue integration, allows event listeners to catch up on missed events in a separate PHP process rather than synchronously during the main request. This change introduces an asynchronicity boundary, improving performance and responsiveness for event sourcing workflows by offloading the catch-up logic to the job queue.

Classes/EventPublisher/JobQueue · high confidence

Event listeners can now hook into invocation and catch-up lifecycle

Event listeners can now implement new interfaces to execute logic at specific points in the event processing pipeline. The BeforeInvokeInterface and AfterInvokeInterface allow listeners to run code immediately before and after handling a specific event, receiving the EventEnvelope for context. Additionally, the AfterCatchUpInterface enables listeners to perform batched actions once per catch-up batch, which is useful for optimizing side-effects like secondary system updates. Listeners can also provide their own storage mechanism for tracking applied events by implementing ProvidesAppliedEventsStorageInterface, decoupling the sequence number tracking from the default Doctrine-based implementation.

Classes/EventListener · high confidence

Event listeners can now track applied events with concurrency control

Event listeners are now able to track their progress by storing the highest applied event sequence number, which prevents concurrent execution of the same listener. This is implemented via a new \AppliedEventsStorageInterface\ contract and a default \DoctrineAppliedEventsStorage\ that uses database-level locking (SELECT FOR UPDATE) to ensure only one process handles the events at a time. A new \AppliedEventsLog\ entity stores the state in the database, allowing listeners to safely resume from where they left off.

Classes/EventListener/AppliedEventsStorage · high confidence

Introduce Event Publisher subsystem for notifying listeners

This change adds the Event Publisher component within the Neos.EventSourcing package, providing a set of classes to handle event distribution. It introduces the EventPublisherInterface and EventPublisherFactoryInterface contracts, along with implementations including DefaultEventPublisherFactory, DeferEventPublisher (which batches events until request shutdown), JobQueueEventPublisher (which queues CatchUpEventListenerJobs for affected listeners to ensure exactly-once delivery), and NoopEventPublisher. This allows the system to notify third parties, such as Projectors, when new events are committed to the Event Store.

Classes/EventPublisher · high confidence

Introduce core event domain model and type resolution

This change introduces the foundational classes for the event sourcing module within the \Neos.EventSourcing\ package. It adds the \DomainEventInterface\ as a marker for domain events and the \DomainEvents\ collection class, which provides immutable methods to create, append, filter, and map event sets. A new \DecoratedEvent\ class allows wrapping events with metadata and identifiers (such as causation and correlation IDs). Additionally, it implements the \EventTypeResolverInterface\ and its default \EventTypeResolver\, which maps event class names to unique type identifiers (e.g., \PackageKey:ShortClassName\) using reflection, while supporting custom identifiers via the \ProvidesEventTypeInterface\.

Classes/Event · high confidence

Introduce projection management and replay capabilities

This change introduces the core projection management infrastructure, including the \Projection\ DTO, \ProjectionManager\, and \ProjectorInterface\. Users can now manage projections via the \ProjectionManager\, which supports replaying events for specific projections (including replaying up to a specific sequence number) and catching up on new events. The \ProjectorInterface\ extends \EventListenerInterface\ and adds a \reset()\ method, allowing projectors to clear their state before a replay operation.

Classes/Projection · high confidence

Introduction of Event Sourcing Aggregate Root and Recording Interface

This change introduces the core infrastructure for event sourcing within the application. A new \AbstractEventSourcedAggregateRoot\ class has been added to manage the state and event history of aggregates, providing methods to record domain events (\recordThat\), retrieve uncommitted events (\pullUncommittedEvents\), and reconstruct aggregate state from an event stream (\reconstituteFromEventStream\). It also includes an \EventRecordingInterface\ to standardize event recording behavior. The implementation uses strict types and final methods to ensure consistent behavior across all aggregate roots.

Classes · high confidence

Introduction of EventStorageInterface for storage adapters

A new EventStorageInterface has been added to define the contract for event storage adapters. This interface specifies methods for loading events from a stream based on a minimum sequence number, committing events with optimistic concurrency control via an expected version, checking the storage status, and setting up the required database tables. This change establishes the foundational API for interacting with different event store backends.

Classes/EventStore/Storage · high confidence

New CLI commands for Event Store setup/status and Projection management

This change introduces two new CLI command controllers in the Neos.EventSourcing package. The EventStoreCommandController adds commands to set up Event Store backends (creating tables/validating config) and check their connection status. The ProjectionCommandController provides commands to list, describe, replay (with optional sequence number limits), and catch up projections, including support for replaying all projections at once. These commands facilitate managing the event sourcing infrastructure and debugging projection state.

Classes/Command · high confidence

New Doctrine connection factory for event store storage

A new ConnectionFactory class has been added to the Doctrine event store storage layer. This component handles the creation of Doctrine DBAL connections by merging default Flow database configuration with specific backend options, and allows for the registration of custom type mappings (such as flow\_json\_array) to ensure compatibility with the event store's data requirements.

Classes/EventStore/Storage/Doctrine/Factory · high confidence

New Doctrine-based Event Store implementation with optimized streaming and concurrency handling

This change introduces the DoctrineEventStorage and DoctrineStreamIterator classes, providing a new database-backed storage layer for the Event Sourcing package. For users, this means events are now persisted to a relational database using JSON for payloads and metadata, with specific columns for correlation and causation identifiers. The implementation improves performance for large event stores by using batched iteration (100 records per batch) and avoiding expensive OFFSET queries in favor of sequence-number-based pagination. It also adds robust concurrency control, automatically retrying commits on unique constraint violations with exponential backoff, and ensures transaction integrity by rolling back on errors or detecting active transactions.

Classes/EventStore/Storage/Doctrine · high confidence

Removals

Removal of EventStore and EventSerializer components

The EventStore subsystem has been removed from this location. This includes the deletion of the EventSerializer and its interface, the EventSourcedRepository, the EventStore and its interface, the EventStorageInterface, and supporting classes such as EventStream, EventStreamData, and related exception classes (ConcurrencyException, EventSerializerException, EventStreamNotFoundException).

Classes/Flowpack/Cqrs/EventStore · high confidence

Removal of initial CQRS domain and query infrastructure

The initial implementation of the CQRS (Command Query Responsibility Segregation) package has been removed. This change deletes the core domain components, including the \AggregateRootInterface\, \AggregateRootTrait\, \RepositoryInterface\, and value objects like \Uuid\ and \Timestamp\. It also removes the query-side infrastructure, specifically the \QueryBus\, \QueryHandlerInterface\, \QueryInterface\, and related exception classes. Users relying on this initial version of the CQRS package will no longer have access to these foundational classes.

Classes/Flowpack/Cqrs, Classes/Flowpack/Cqrs/Domain, Classes/Flowpack/Cqrs/Query · high confidence

Removal of legacy CQRS event handling components

The \Classes/Flowpack/Cqrs/Event\ directory has been completely removed, deleting the \EventBus\, \EventHandlerLocator\, \EventHandlerInterface\, \EventInterface\, \FaultInterface\, \GenericFault\, and \EventBusException\ classes. This eliminates the previous implementation of the event bus and its handler registration mechanism from the CQRS module.

Classes/Flowpack/Cqrs/Event · high confidence

Removal of legacy CQRS message infrastructure

The CQRS message subsystem has been removed, deleting the core interfaces and classes that previously handled message definition, metadata, and resolution. Specifically, this change removes the MessageInterface, MessageBusInterface, MessageResultInterface, and MessageMetadata classes, along with the MessageTrait used to implement message behavior. It also deletes the exception classes (MessageBusException, MessageHandlerException) and the handler resolution logic, including the ResolverInterface and the AutoResolver implementation. This eliminates the ability to define, dispatch, and resolve messages using the previous architecture.

Classes/Flowpack/Cqrs/Command, Classes/Flowpack/Cqrs/Message · high confidence

Behavioural changes

Database schema updates for event sourcing and process management

This update introduces and modifies database tables to support event sourcing and process management features. It adds a new table for tracking projection state in Doctrine-based projectors and another for logging applied events from event listeners. The schema also includes a table for storing process manager state, which was later renamed to 'process state' and subsequently dropped as it became obsolete. Additionally, table names were renamed to reflect the package rename from Neos.Cqrs to Neos.EventSourcing.

Migrations · high confidence

Major rework of the EventStore implementation

The EventStore has been completely restructured to support multiple backends, unique event identifiers, and enriched metadata. The new API introduces an EventStoreFactory for configuring storage backends, an EventNormalizer for serializing events, and a StreamName value object that supports category and correlation streams. Events are now stored with unique UUIDs and metadata, and the EventStream iterator returns EventEnvelope objects containing both the raw event data and the converted domain event.

Classes/EventStore · high confidence

New event-to-listener mapping infrastructure

The system now uses a dedicated mapping layer to configure which event listeners handle specific domain events. This change introduces \DefaultEventToListenerMappingProvider\ to read listener patterns from the \Neos.EventSourcing.EventStore.stores\ configuration and automatically match them against available listeners. It also adds \EventToListenerMapping\ and \EventToListenerMappings\ value objects to represent these relationships, ensuring that every listener is explicitly registered to an event store and providing clearer error messages when configurations are invalid or listeners are unused.

Classes/EventListener/Mapping · high confidence

New exception classes for event listener errors

The event sourcing system now includes specific exception classes to handle listener-related failures. \EventCouldNotBeAppliedException\ provides context by exposing the failed event envelope and the affected listener interface, while \HighestAppliedSequenceNumberCantBeReservedException\, \InvalidConfigurationException\, and \InvalidEventListenerException\ cover sequence reservation, configuration validity, and handler method mismatches respectively.

Classes/EventListener/Exception · high confidence

Support for Flow proxy classes and value object normalization in Event Store

The Event Store normalizer now correctly handles Neos Flow proxy classes by delegating constructor resolution to the parent class, preventing deserialization failures for proxied objects. Additionally, a new ValueObjectNormalizer has been introduced to support denormalizing scalar values (strings, integers, floats, booleans, and arrays) into value objects that use either a single-argument constructor or a static named constructor (e.g., \fromString\, \fromArray\) with appropriate return types, enhancing flexibility for event data reconstruction.

Classes/EventStore/Normalizer · high confidence

Test coverage

Added functional tests for event sourcing components; Added test fixtures for event normalization scenarios; Added unit tests for DecoratedEvent metadata and identifier handling; Added unit tests for DefaultEventToListenerMappingProvider validation; Added unit tests for DeferEventPublisher and JobQueueEventPublisher; Added unit tests for EventListenerInvoker; Added unit tests for EventNormalizer and StreamName; Added unit tests for InMemoryStreamIterator; Added unit tests for ProjectionManager; Added unit tests for ValueObjectNormalizer.

Dependencies

Package renamed to Neos.EventSourcing with major dependency and framework updates

The package has been renamed from flowpack/cqrs to neos/event-sourcing and updated to require PHP 8.0 or higher, along with Neos Flow 7.0, 8.0, or 9.0. It now includes symfony/serializer and symfony/property-access as dependencies, uses ramsey/uuid for UUID generation, and adopts PSR-4 autoloading under the Neos\\EventSourcing namespace.

(dependencies) · high confidence

Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.

How this codebase got here

Score

  • CAI 51 → 52 (+0.7)
  • Rubric changed (rubric-2026.08.19 → rubric-2026.09.15) — scores are not directly comparable.

Lenses

  • Code Health 96 → 97 (+1.4)
  • Architecture 100 → 97 (-2.7)
  • Maturity 53 → 53 (+0.1)
  • Readiness 24 → 26 (+1.4)
  • Security 100 → 100 (+0.0)

Resolved (12)

  • Coverage not included — suite not readable by the collector
  • Dependency hygiene not measured — dependency manifest found but not parsed for hygiene
  • Dormant codebase
  • Duplicated block (11 lines × 2) (Classes/Command/ProjectionCommandController.php)
  • Duplicated block (11 lines × 2) (Classes/EventStore/Storage/Doctrine/DoctrineStreamIterator.php)
  • Duplicated block (6 lines × 2) (Classes/Command/EventStoreCommandController.php)
  • Duplicated block (8 lines × 2) (Classes/Command/ProjectionCommandController.php)
  • Duplicated block (8 lines × 2) (Classes/EventStore/Storage/Doctrine/DoctrineEventStorage.php)
  • Duplicated block (9 lines × 2) (Classes/Command/ProjectionCommandController.php)
  • No exposed public API
  • Off-boarding risk: anonymized user #1
  • Test reliability not included

New (14)

  • Dependency hygiene PARTLY measured — Composer dependencies read, no committed lock to grade for currency
  • Duplicated block (12 lines × 2) (Classes/EventStore/Storage/Doctrine/DoctrineEventStorage.php)
  • Duplicated block (14 lines × 2) (Classes/Command/ProjectionCommandController.php)
  • Duplicated block (14 lines × 2) (Classes/Command/ProjectionCommandController.php)
  • Duplicated block (15 lines × 2) (Classes/EventStore/Storage/Doctrine/DoctrineStreamIterator.php)
  • Duplicated block (21 lines × 4) (Migrations/Mysql/Version20161114151203.php)
  • Duplicated block (23 lines × 2) (Migrations/Mysql/Version20161117145045.php)
  • Duplicated block (6–7 lines × 2) (Classes/Command/EventStoreCommandController.php)
  • Duplicated block (7 lines × 2) (Classes/Command/ProjectionCommandController.php)
  • Duplicated block (7 lines × 2) (Classes/Command/ProjectionCommandController.php)
  • Off-boarding risk: anonymized user #1
  • Orphaned files with no living knowledge
  • TodoComment (Classes/EventStore/EventNormalizer.php)
  • TodoComment (Classes/EventStore/EventNormalizer.php)

Changes since last survey

  • 2 commits — 0 feature/other, 2 fixes

By area

  • (repo) — 1 commit
  • Classes/Command — 1 commit

Notable commits

  • fix: BUGFIX: Declare implicitly nullable parameters explicitly
  • fix: Merge pull request #324 from neos/bugfix/php84-implicit-nullable-parameters

Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.

Survey your own repository

neos/Neos.EventSourcing was measured the same way every project in this corpus was: the same rubric, at a pinned commit, with the result published in full. Point a surveyor at a repository you know and see whether you agree with it.

About this page

  • The score is its most recent published measurement, taken on 21 September 2026 at a pinned commit. It is not a live figure and does not change until the project is measured again.
  • Measured at commit c34ad2bed1f80ac7f853abb359f0aa7a295a1bc3 — the exact code this score is about.
  • Scored under rubric-2026.09.15 — the same rubric and the same method as every other entry in this index.
  • Measured by watchdog.canine.dev using codehealth-analyzer preprod-fa71c66cabd8.