Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Event Sourcing Symfony Bridge Laravel Package

neos/event-sourcing-symfony-bridge

Symfony bridge for Neos Event Sourcing, wiring event stores, projections, and subscribers into the framework. Provides dependency injection, console commands, and configuration to run event-sourced applications smoothly on Symfony.

View on GitHub
Deep Wiki
Context7

Getting Started

This bridge wires the standalone neos/event-sourcing library into a Symfony app: it registers the event store, auto-wires listeners and projections, and exposes CLI commands for replay. It is thin glue, not a framework — you still model events, listeners, and read models yourself.

  1. Install and register the bundle

    composer require neos/event-sourcing-symfony-bridge
    

    Enable it in config/bundles.php (namespace is Neos\EventSourcing\SymfonyBridge\...). Run bin/console config:dump-reference for the bundle and bin/console debug:container --tags to see exactly which tags it registers — the package has few stars and no recent releases, so verify names against the installed version rather than trusting blog posts.

  2. Configure the event store connection. You need a Doctrine DBAL connection and a table (often created via a migration or the bundle's schema command, if present). Point the bridge at your connection and table name in config/packages/*.yaml; use a separate connection if you want event writes isolated from your app's main transactional workload.

  3. Look first at three things: the bundle's DependencyInjection/ extension (what config keys and tags exist), its Command/ directory (replay/reset/setup commands), and the upstream neos/event-sourcing interfaces (EventStore, EventListenerInterface, ProjectionInterface). The bridge only makes sense once you understand those contracts.

  4. First use case: a rebuildable read model. Define one event (UserRegistered), one projection that writes to a users read table, append the event from a controller or command handler, and call the projection synchronously. Then run the replay command against that projection to prove you can rebuild it from scratch. That loop — append → project → replay — is the entire mental model of this package; everything else is scale and plumbing.

Implementation Patterns

Append events from your application services, not from entities. The typical flow is: your command handler validates input, constructs a plain event DTO, and calls the event store. The bridge's store service is the single write point; keep it out of Doctrine entities and out of controllers so you can later swap synchronous publishing for async.

Two consumer styles, both tag-based.

  • Event listeners (EventListenerInterface) run on publish and are for side effects — sending mail, dispatching to a queue, updating a small lookup. They are not rebuildable in practice; never put your primary read model here.
  • Projections (ProjectionInterface) are the rebuildable read models. They get the full event stream on replay. Put every table you would want to regenerate here, and give each projection its own reset() that truncates only its own tables.

Register both via the bundle's service tags rather than by service ID — autoconfiguration won't find them, since they implement upstream interfaces the framework doesn't know about.

Synchronous first, async later. Get the projector working inline, then introduce an async publisher (Symfony Messenger or the library's async decorator) once write latency matters. Because projections are replayable, switching from sync to async is a config change plus a replay — design for that from day one and you avoid rewrites.

Replay as a routine operation, not an incident response. Before each deploy that changes a projection's schema, run reset + replay in staging, measure duration, and record the event-store size. In production, replay into a shadow table and swap when the CLI command is available; otherwise schedule a maintenance window. The bridge's replay commands are the main operational tool you'll use week to day.

Event versioning. Treat the event class name + payload as a contract. When a field must change, add a new event version and an upcaster or a new listener branch; do not mutate the old event class. The store serializes to JSON by default, so add a discriminator (eventVersion or a v2 class) and keep both readable.

Integration tips.

  • Use a dedicated DBAL connection for the event store to avoid accidental transactions spanning your write model and your events.
  • Wrap command handlers with a transactional boundary only around the append; projections and listeners should run after commit or idempotently.
  • Give every projection a unique name and a reset(); that name is what the replay command targets.
  • Expose a small internal API (a thin EventPublisher service) so the rest of the app never touches the bridge directly — that is your seam if the package is abandoned.

Gotchas and Tips

  • Maintenance risk is the headline. Last release 2022, 4 stars, zero dependents. Treat it as glue code you may need to vendor or fork. Pin the version, and keep the upstream neos/event-sourcing version locked alongside it — the bridge is version-coupled and has historically lagged DBAL major versions.
  • Doctrine DBAL compatibility. Check that the bridge supports your DBAL major (Doctrine 3.x vs 2.x changed result APIs). If composer update fights you on DBAL, that is why. Test on a fresh install before touching a legacy one.
  • Silent no-op consumers. Services implementing the upstream interfaces but not tagged simply never fire. Confirm with debug:container --tag=<tag> — this is the single most common "why isn't my projection running" cause.
  • Replay requires a clean reset. Replay alone does not truncate; if your projection is not idempotent, running replay twice duplicates rows. Always reset then replay, and make every projection upsert-safe anyway.
  • Event serialization is unforgiving. Serialize plain scalars and nested DTOs. DateTime and value objects need an explicit normalizer or you get inconsistent payloads between write and read. Avoid closures, resources, and circular references.
  • No atomicity across store and read model. The event table and your projection tables live in different transactions. A crash between append and project leaves a stale read model — this is expected. Your recovery is replay, not two-phase commit. Build projection offsets/last-processed tracking if you need to resume rather than replay.
  • Async retries need poison-event handling. With Messenger, configure retry_strategy and a failure transport. A projection that throws on a bad event will loop forever otherwise; add a dead-letter path and a command to skip or fix the offset.
  • Config quirks. Connection name, table name, and event-type resolution settings are separate keys; a typo in the connection key often falls back to the default connection and writes events into your app database. Inspect the generated table after first run.
  • Extension points worth knowing: decorate the event publisher (for logging, outbox, or async), provide a custom EventStore implementation (e.g., Postgres-native) by aliasing the interface, and implement EventTypeResolver if you want class FQCNs decoupled from stored type strings. These are the three seams that let you outlive the package itself.
  • Debugging tip: enable the bridge's logging middleware/decorator and log every append (event type + aggregate id + sequence) and every listener invocation. Because failures are silent, a structured log is your only signal that a listener was skipped vs errored.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource