Skip to content
CAI
Software that uses CAICheck a score

proophsoftware/event-machine

51.4

Adequate · 20 September 2026

11.3k

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 a PHP framework for building event-sourced applications, providing a structured runtime for managing command, event, and query processing. It supports multiple programming styles through functional, object-oriented, and prototyping 'flavours' that allow developers to define aggregates and handle messages with type-safe, immutable data structures. The framework includes a built-in projection system for maintaining read models, a PSR-15 compliant HTTP interface for exposing services, and comprehensive in-memory persistence layers to facilitate testing and lightweight deployments.

How it got here

2017 — Rebranding to Event Machine and core refactoring

20 changes.

The project was rebranded from Prooph Workshop to Event Machine, involving the removal of legacy workshop infrastructure and a comprehensive refactoring of the core event sourcing engine. This period introduced structured environments, immutable data records, and query support while modernizing dependencies and aligning with PSR-15 standards.

2018 — Core infrastructure and projection system

21 changes.

This period focused on building the foundational persistence, projection, and querying layers of the Event Machine, introducing in-memory implementations and comprehensive document store filtering. It also established a flexible runtime architecture supporting multiple programming styles through the new Flavour interface and OOP/functional examples.

Features

Add PrototypingFlavour example with user aggregate and query resolvers

The examples/PrototypingFlavour directory now includes a complete event-sourced user management example. It defines a User aggregate with registration and username change capabilities, supporting both closure-based and cacheable description styles. The example registers commands and events with JSON schemas, implements synchronous and asynchronous query resolvers for retrieving user data, and includes a projector to maintain a read model of registered users.

examples/PrototypingFlavour · high confidence

Added in-memory implementations for event projection and querying

This change introduces a new \InMemory\ namespace under \src/Projecting\ containing concrete implementations for the event projection and query interfaces. Specifically, it adds \InMemoryEventStoreProjector\, \InMemoryEventStoreQuery\, \InMemoryEventStoreReadModelProjector\, and \InMemoryProjectionManager\. These classes provide an in-memory emulation of transaction behavior for projections and queries, allowing developers to test projection logic without a persistent event store backend. The \InMemoryProjectionManager\ acts as a factory for these in-memory components, managing projector instances and providing methods to fetch projection names and statuses within the in-memory context.

src/Projecting/InMemory · high confidence

FunctionalFlavour example demonstrates custom message handling and event-machine integration

The FunctionalFlavour example now provides a complete, working implementation of the Prooph Event Machine pattern, featuring a User aggregate that handles registration and username changes via custom commands and events. It introduces a functional port (ExampleFunctionalPort) that serializes and deserializes custom message objects, allowing the event machine to process domain-specific types. The example also includes resolvers for synchronous and asynchronous queries, a projector for building read models from events, and a process manager for side effects, illustrating how to wire these components together using closures and custom message descriptions.

examples/FunctionalFlavour · high confidence

Introduce OOP-flavoured User aggregate example

The examples/OopFlavour directory now includes a complete Object-Oriented implementation of a User aggregate, demonstrating how to define aggregates, descriptions, and ports in the OOP style. This includes the User aggregate class handling registration and username changes, a UserDescription class wiring commands to aggregate methods, and an ExampleOopPort implementing the runtime interface to invoke the aggregate.

examples/OopFlavour · high confidence

Introduce domain-specific exception hierarchy

The library now provides a structured set of exceptions under the Prooph\\EventMachine\\Exception namespace, including a base EventMachineException interface and specific classes for invalid arguments, invalid event formats, missing aggregate identifiers, and transaction failures (commit, rollback, not started). This allows users to catch and handle event-machine-specific errors more precisely than relying on generic PHP exceptions.

src/Exception · high confidence

Introduce in-memory document store with indexing and ordering support

The \src/Persistence/DocumentStore\ area now includes an in-memory implementation of the document store interface, enabling local persistence and querying without an external database. This change adds support for defining field and multi-field indexes, filtering documents, and sorting results by ascending or descending order on specific properties, including nested properties accessed via dot notation. The \InMemoryDocumentStore\ class provides full CRUD operations (add, update, delete, upsert) and collection management, serving as a transaction-emulating backend for testing or lightweight scenarios.

src/Persistence/DocumentStore · high confidence

Introduce query handling infrastructure with async/sync resolvers and description

The src/Querying directory now provides the core components for handling queries within the Event Machine. This includes the QueryDescription class for defining query metadata (name, resolver, return type), and two resolver interfaces: SyncResolver for blocking operations and AsyncResolver for non-blocking operations using React Promises. Additionally, the GenericJsonSchemaQuery class allows queries to be treated as JSON schema messages, and the QueryConverterBusPlugin integrates these resolvers with the Prooph ServiceBus QueryBus, ensuring that query handlers are properly decorated to support both synchronous and asynchronous resolution patterns.

src/Querying · high confidence

Introduction of ImmutableRecord data structures and conversion logic

The src/Data directory now includes the core infrastructure for immutable value objects, featuring the ImmutableRecord interface, the ImmutableRecordLogic trait for implementing these records, and the ImmutableRecordDataConverter for serializing them. This change enables users to define strongly-typed, immutable data structures that integrate with the event machine's JSON schema validation and data conversion pipelines.

src/Data · high confidence

Introduction of Query support and typed message interfaces

The messaging layer now supports Queries alongside Commands and Events. A new \Message\ interface defines \get\ and \getOrDefault\ methods for payload access, implemented by \MessageBag\ and \GenericJsonSchemaMessage\. The \GenericJsonSchemaMessageFactory\ has been extended to accept a \queryMap\ and \definitions\, allowing it to instantiate \GenericJsonSchemaQuery\ objects and validate their payloads against schemas. Additionally, message names are now strictly validated against a regex pattern, and unknown message names trigger a 404 (Not Found) status code via a \RuntimeException\.

src/Messaging · high confidence

New document store filter types and logic

The DocumentStore now supports a comprehensive set of new filter implementations, including logical combinators (AndFilter, OrFilter, NotFilter), comparison operators (EqFilter, GtFilter, GteFilter, LtFilter, LteFilter, InArrayFilter), pattern matching (LikeFilter), existence checks (ExistsFilter), and a wildcard (AnyFilter). These filters enable more complex querying capabilities, such as nested property access via dot notation and case-insensitive string matching, allowing users to filter documents with greater precision and flexibility.

src/Persistence/DocumentStore/Filter · high confidence

New event-machine projection system with aggregate and custom event support

The \src/Projecting\ directory now introduces a complete projection framework for the Event Machine. This includes an \AggregateProjector\ that automatically maintains read models by loading aggregate state and upserting it to a document store, supporting deletion when aggregates are marked as deleted. A new \CustomEventProjector\ interface allows handling mixed event types, while \ProjectionDescription\ provides a fluent API to define projections with filters for aggregate types and specific events. The \ProjectionRunner\ and \ReadModelProxy\ manage the lifecycle of these projections, integrating with the underlying projection manager. Additionally, \FlavourAware\ enables projectors to receive runtime flavour context, and \ReadModel\ acts as a wrapper to apply filters and delegate handling to the appropriate projector.

src/Projecting · high confidence

New persistence interfaces and in-memory transactional implementation

This change introduces a new persistence layer within the Event Machine, providing both abstractions and an in-memory implementation to support immediate consistency and transactional behavior. New interfaces include \AggregateStateStore\ for loading aggregate state, \DeletableState\ to signal when read-model state should be deleted, and \DocumentStore\ for managing document collections with filtering and ordering capabilities. The \TransactionalConnection\ interface and \TransactionManager\ class enable nested transaction support (begin, commit, rollback) across the system. Additionally, \InMemoryConnection\ and \InMemoryEventStore\ provide an in-memory emulation of these transactional behaviors for testing or lightweight scenarios, while the \Stream\ class allows for custom write model stream naming.

src/Persistence · high confidence

New reflection-based service container and test environment support

The container subsystem now includes a ReflectionBasedContainer that automatically maps service factories to their return types, including a check to prevent duplicate return types. A new ContextProviderFactory simplifies the creation of context providers, while the renamed EventMachineContainer (formerly FactoriesContainer) now explicitly supports message factory and JSON schema assertion services. Additionally, a TestEnvContainer provides an in-memory implementation of core services like the event store, projection manager, and document store to facilitate testing without external dependencies.

src/Container · high confidence

Removals

Removal of Prooph Workshop application code and infrastructure

The application's core domain logic, infrastructure, and supporting assets have been removed. This includes the event-sourced models (User, Configuration), command handlers, and domain events, as well as the infrastructure layer comprising the dependency factories, HTTP router, error logging, and MongoDB connection handling. Additionally, the database initialization scripts for PostgreSQL (event streams and projections tables) and the public-facing WebSocket test page (ws.html) and client library (stomp.min.js) have been deleted, effectively stripping the repository of the Prooph Workshop implementation.

(repo-wide) · high confidence

Removal of quick\_start example script

The quick\_start.php example file has been removed from the examples directory. This file previously demonstrated how to initialize the EventMachine and load the Aggregate configuration, meaning users can no longer reference this specific script for a quick-start setup.

examples · high confidence

Removed UserDescription aggregate example

The UserDescription example file in the examples/Aggregate directory has been deleted. This removes the specific event machine description and aggregate logic for user registration and username changes that was previously demonstrated in this location.

examples/Aggregate · high confidence

Behavioural changes

Aggregate root and translator now use Flavour for message conversion and apply logic

The \GenericAggregateRoot\ and \ClosureAggregateTranslator\ classes now accept a \Flavour\ instance to handle message serialization and deserialization. During history replay, events are converted from network format using \Flavour::convertMessageReceivedFromNetwork\, and pending events are prepared for transmission via \Flavour::prepareNetworkTransmission\. Additionally, the \apply\ methods for state changes are invoked through \Flavour::callApplyFirstEvent\ and \Flavour::callApplySubsequentEvent\, allowing the flavour to control how aggregate state is updated. The \GenericAggregateRoot\ also now implements \AggregateTypeProvider\ to expose its aggregate type.

src/Aggregate · high confidence

Command handling refactored to support context providers and pre-processors

The command processing pipeline has been restructured to allow injecting runtime context into aggregate handlers and applying transformations before commands are processed. A new \CommandPreProcessor\ interface enables intercepting and modifying incoming command messages, while the \CommandProcessorDescription\ now supports a \provideContext\ method to specify a context provider. Consequently, the \CommandProcessor\ accepts a \ContextProvider\ and uses it to supply state to aggregate functions, and the \CommandToProcessorRouter\ wires these dependencies together during message bus attachment.

src/Commanding · high confidence

Event listener decoration and recorder description invocation

The EventConverterBusPlugin now intercepts event dispatch to wrap non-MessageProducer listeners, ensuring they are invoked through the system's flavour handler. Additionally, EventRecorderDescription now implements \_\_invoke to return a structured array of the recorded event and its apply function, while its apply method has been tightened to strictly enforce that only one apply callback can be assigned per event.

src/Eventing · high confidence

Introduce OOP flavour runtime components

The Oop runtime location now includes the core classes required for the OOP flavour: the \Port\ interface defining the contract for aggregate factory calls, command handling, event recording, and serialization; the \FlavourHint\ class which throws an exception if invoked outside the correct OOP context; and the \AggregateAndEventBag\ immutable DTO used to pass newly created aggregate instances along with their first event to the apply method within the message bag.

src/Runtime/Oop · high confidence

Introduce dedicated JSON Schema type classes for validation and annotations

The \src/JsonSchema/Type\ directory now contains specific implementation classes for JSON Schema types (such as \ArrayType\, \BoolType\, \EmailType\, \EnumType\, \FloatType\, \IntType\, \ObjectType\, \StringType\, \TypeRef\, and \UuidType\). These classes implement the \AnnotatedType\ interface and utilize traits like \NullableType\ and \HasAnnotations\ to support schema generation with validation constraints (e.g., \minimum\, \maximum\, \minLength\, \pattern\) and metadata (title, description). This change replaces previous generic handling with a structured, object-oriented approach to defining JSON Schema structures, allowing users to build complex schemas with precise type validation and optional nullability.

src/JsonSchema/Type · high confidence

Major refactoring of EventMachine core and introduction of utility classes

The EventMachine core class has been significantly refactored to implement the MessageDispatcher and AggregateStateStore interfaces, introducing a structured environment system (prod, dev, test) and standardized service IDs for internal components like the event store, command bus, and projection manager. This change also adds new utility classes, DetermineVariableType and MapIterator, to support internal type handling and iteration logic, while updating the EventMachineDescription class to align with these structural improvements.

src · medium confidence

MessageBox adopts PSR-15 request handling and EventMachine dispatch

The HTTP message box now implements the PSR-15 \RequestHandlerInterface\ instead of the legacy \MiddlewareInterface\, aligning with modern server-side request handling standards. Internally, it replaces the previous \CommandBus\ and \EventBus\ with a unified \EventMachine\ for dispatching messages, allowing for more consistent event and command processing. The component also introduces support for asynchronous responses via React Promises, automatically converting results to JSON when available, and ensures every message includes a generated UUID and creation timestamp.

src/Http · high confidence

Project rebranded to Event Machine with deprecation notice and CI setup

The project has been renamed from 'prooph software Event Sourcing Workshop' to 'Event Machine', with the README explicitly stating it is superseded by Event Engine and directing users to the new product. The repository now includes a Travis CI configuration for PHP 7.1 and 7.2, along with Coveralls integration for test coverage reporting. Additionally, the local Docker Compose environment and Postman collection have been removed, and the documentation source has been moved to a separate repository.

(repo-wide) · high confidence

Runtime Flavours now support custom events and messages

The \src/Runtime\ layer has been refactored to introduce a \Flavour\ interface and three concrete implementations (\PrototypingFlavour\, \FunctionalFlavour\, \OopFlavour\) that allow Event Machine to communicate with domain models using custom message types. This change adds support for invoking event listeners and projectors with custom events, and allows resolvers to handle custom messages via a new \SyncResolver\. Users can now implement a \Port\ to map between Event Machine's generic messages and their own type-safe message structures, enabling more decoupled and type-safe domain models.

src/Runtime · high confidence

Switch JSON schema validation to JustinRainbow and introduce typed schema builders

The JSON schema validation backend has been replaced from Webmozart to JustinRainbow, with the new validator now requiring an object name for clearer error reporting. Additionally, the schema definition API has shifted from returning raw arrays to using a typed object model (e.g., ObjectType, StringType), providing a more structured way to define and validate JSON schemas.

src/JsonSchema · high confidence

Test coverage

Added HTTP MessageBox tests; Added test coverage for EventMachine flavours and test mode; Added test coverage for in-memory projection components; Added test stubs for ImmutableRecord and value objects; Added test stubs for context-aware aggregate processing; Added tests for DocumentStore filter implementations; Added tests for DocumentStore indexing and in-memory persistence; Added tests for ImmutableRecord value objects; Added tests for JSON Schema message factory and message payload access; Added tests for ReflectionBasedContainer; Added tests for in-memory persistence and transaction management; Added unit tests for CommandProcessor and CommandToProcessorRouter; Added unit tests for JsonSchema Type classes; Updated tests for GenericAggregateRoot to use Flavour and Message objects.

Dependencies

Project renamed to Event Machine and dependencies modernized

The project has been renamed from 'es-workshop' to 'event-machine' and shifted from an opinionated Event Sourcing workshop to a framework based on prooph components. Several dependencies were removed, including MongoDB support (prooph/mongodb-snapshot-store, mongodb/mongodb), AMQP production (prooph/humus-amqp-producer), and legacy HTTP middleware (http-interop/http-middleware). The dependency list was updated to include PSR-15 support (psr/http-server-middleware), JSON schema validation (justinrainbow/json-schema), and other utilities, while the lock file was removed to allow flexible version resolution.

(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 58 → 51 (-6.3)
  • Rubric changed (rubric-2026.08.17 → rubric-2026.09.15) — scores are not directly comparable.

Lenses

  • Code Health 93 → 95 (+2.4)
  • Architecture 98 → 82 (-15.9)
  • Maturity 37 → 37 (+0.0)
  • Readiness 57 → 39 (-17.3)
  • Security 100 → 100 (+0.0)

Resolved (25)

  • Change coupling: AggregateProjector.php ↔ ReadModel.php (src/Projecting/AggregateProjector.php)
  • Change coupling: CommandProcessor.php ↔ CommandProcessorDescription.php (src/Commanding/CommandProcessor.php)
  • Coverage not included — suite not readable by the collector
  • Dependency hygiene not measured — no supported dependency manifest was read
  • Duplicated block (11 lines × 2) (src/Http/MessageBox.php)
  • Duplicated block (12 lines × 2) (src/Runtime/PrototypingFlavour.php)
  • Duplicated block (13 lines × 2) (src/Persistence/InMemoryEventStore.php)
  • Duplicated block (13 lines × 2) (src/Runtime/FunctionalFlavour.php)
  • Duplicated block (14 lines × 2) (src/Commanding/CommandProcessor.php)
  • Duplicated block (14 lines × 3) (src/Projecting/InMemory/InMemoryEventStoreProjector.php)
  • Duplicated block (14–15 lines × 2) (src/Persistence/InMemoryEventStore.php)
  • Duplicated block (14–15 lines × 2) (src/Projecting/InMemory/InMemoryEventStoreProjector.php)
  • Duplicated block (15 lines × 2) (src/Projecting/InMemory/InMemoryEventStoreProjector.php)
  • Duplicated block (15 lines × 2) (src/Projecting/InMemory/InMemoryEventStoreProjector.php)
  • Duplicated block (15 lines × 2) (src/Projecting/InMemory/InMemoryEventStoreReadModelProjector.php)
  • Duplicated block (16 lines × 2) (src/Projecting/InMemory/InMemoryEventStoreProjector.php)
  • Duplicated block (18 lines × 3) (src/Projecting/InMemory/InMemoryEventStoreProjector.php)
  • Duplicated block (18–19 lines × 2) (examples/FunctionalFlavour/Api/MessageDescription.php)
  • Installation points only to an external skeleton repo ('https://github.com/proophsoftware/event-machine-skeleton') without showing how to clone or run the project. (README.md)
  • No exposed public API
  • …and 5 more

New (28)

  • ClassTooLong: EventMachine (src/EventMachine.php)
  • Dependency hygiene PARTLY measured — Composer dependencies read, no committed lock to grade for currency
  • Documentation: no installation or build instructions (README.md)
  • Documentation: no usage examples (README.md)
  • Duplicated block (11 lines × 2) (src/Runtime/PrototypingFlavour.php)
  • Duplicated block (13 lines × 2) (src/Http/MessageBox.php)
  • Duplicated block (13 lines × 2) (src/Runtime/FunctionalFlavour.php)
  • Duplicated block (14–15 lines × 2) (src/Projecting/InMemory/InMemoryEventStoreProjector.php)
  • Duplicated block (15 lines × 2) (src/Projecting/InMemory/InMemoryEventStoreProjector.php)
  • Duplicated block (15 lines × 2) (src/Projecting/InMemory/InMemoryEventStoreReadModelProjector.php)
  • Duplicated block (16 lines × 2) (src/Persistence/InMemoryEventStore.php)
  • Duplicated block (16 lines × 2) (src/Projecting/InMemory/InMemoryEventStoreProjector.php)
  • Duplicated block (16–17 lines × 2) (src/Persistence/InMemoryEventStore.php)
  • Duplicated block (17 lines × 3) (src/Projecting/InMemory/InMemoryEventStoreProjector.php)
  • Duplicated block (17 lines × 3) (src/Projecting/InMemory/InMemoryEventStoreProjector.php)
  • Duplicated block (23 lines × 2) (src/Projecting/InMemory/InMemoryEventStoreProjector.php)
  • Duplicated block (38 lines × 3) (src/Projecting/InMemory/InMemoryEventStoreProjector.php)
  • Duplicated block (46 lines × 2) (src/JsonSchema/Type/FloatType.php)
  • Duplicated block (50 lines × 2) (src/Persistence/DocumentStore/OrderBy/Asc.php)
  • Duplicated block (7 lines × 3) (src/Projecting/InMemory/InMemoryEventStoreProjector.php)
  • …and 8 more

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

Survey your own repository

proophsoftware/event-machine 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 20 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 4f48d1d744b6fd1aabaffb894c4a72e6faa49a02 — 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-28e75b8e3254.