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 Store Bundle Laravel Package

landingi/event-store-bundle

A PHP event store bundle that adds event sourcing infrastructure to your application: append domain events to streams, replay them to rebuild aggregates, and wire the store into your existing framework services with minimal configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install and inspect before wiring anything up.

    composer require landingi/event-store-bundle
    

    Immediately open the installed package's composer.json. This is the single most important step: a package named *-bundle is almost certainly a Symfony bundle, not a Laravel package. Check for symfony/framework-bundle, symfony/dependency-injection, doctrine/*, or a Prooph/Broadway dependency. That tells you whether you can consume it in Laravel at all, or only alongside a Symfony kernel.

  2. Read the DI extension first, not the README. Zero-star, zero-public-dependent packages rarely have complete docs. The real configuration surface lives in DependencyInjection/*Extension.php and Resources/config/*.yaml (or config/). That's where you'll find service IDs, aliases, and which parameters are required versus optional. If a README.md exists, treat it as a sketch and verify every service ID against the extension class.

  3. Locate the core abstractions. Look for the event store interface, the aggregate repository, and the event/stream value objects. Typical names in this space: EventStore, EventStoreInterface, AggregateRepository, StreamName, EventMetadata. Confirm the expected-version/concurrency argument on the append method — that's the contract you'll build around.

  4. First use case: append one event and read it back. Before modeling a full aggregate, write a throwaway script or test that appends a single domain event to a named stream and reads the stream back. This validates serialization, storage adapter (Doctrine/PDO/in-memory), and configuration in one shot. It also flushes out missing migrations or table names early.

  5. Check the tags and the changelog before assuming semver. Read the compare links between the last few tags (e.g. v2.1.0...v2.2.0) rather than trusting the version number. This package has shipped feature removals in a minor bump — v2.2.0 removed the by_support implementation — so treat minor releases as potentially breaking and pin the exact version in composer.json.

Implementation Patterns

Aggregate repository as the write boundary. The day-to-day pattern is: load an aggregate by ID from a stream, call a behavior method that records events, then persist. Keep the repository injected into application services, not controllers. If the bundle exposes an interface, bind it in your container (Laravel service provider or Symfony services.yaml) rather than resolving the concrete class.

Append-with-expected-version for concurrency. Use the expected stream version on every append. This is the mechanism that prevents two concurrent requests from both succeeding on the same aggregate. If the bundle's API doesn't expose it, that's a red flag for production use and you should add an adapter layer that enforces it.

Subscribers/projectors for read models. Expect an event bus or listener mechanism to drive projections. Wire projectors as listeners, and keep them idempotent — track the last processed stream position per projector. Rebuild projections by replaying from position zero; make sure your projector can be truncated and replayed without side effects on external systems (or gate external calls behind an outbox).

Metadata for correlation. Populate event metadata with request/correlation IDs, actor ID, and schema version at append time. This is cheap to add early and expensive to backfill later. If the bundle's event envelope supports metadata, standardize on a small set of keys before you have hundreds of thousands of events.

Snapshots for long streams. If the package supports snapshots, use them only for aggregates with high event counts (hundreds+). Snapshot on a fixed interval (e.g., every 50 events), never on every append, and always keep the full stream as the source of truth.

Laravel integration tip. You won't get auto-discovery. Write a thin service provider that registers the package's classes manually, and only pull in the pieces that don't depend on Symfony's kernel/container internals. If the bundle's services are tightly bound to Symfony\Component\DependencyInjection, the pragmatic path is to run the event store in a Symfony service or a separate process and communicate over HTTP/queue — not to fight the container.

Build only on the core, not the peripheral features. v2.2.0 dropped by_support outright. Anything that isn't the append/load/stream contract — support flags, auxiliary repositories, convenience helpers — is fair game for removal. Keep those usages behind your own interfaces so an upgrade is a one-file change.

Testing pattern. Use an in-memory or SQLite-backed store in tests, and assert on the recorded events rather than on internal state. Given the package's maturity, keep an integration test that pins the storage schema so an upgrade that changes table names fails loudly. Add a smoke test that boots the container and resolves your store service — that catches removed service IDs and deleted config keys immediately.

Gotchas and Tips

  • Symfony bundle ≠ Laravel package. This is the biggest pitfall. The Bundle suffix and the landingi namespace strongly imply Symfony conventions. Verify before planning any Laravel integration; if it requires symfony/http-kernel, budget for a separate service or a fork.
  • v2.2.0 is a breaking change in a minor version. The by_support implementation was removed (PR #27, [DIS-54]). Before upgrading from 2.1.x, grep your code, your DI config, and any YAML/PHP service definitions for by_support, BySupport, and related service IDs — a removed service ID typically fails at container compile time or, worse, at runtime on first resolution. If you depend on that behavior, fork it or stay on 2.1.x pinned.
  • The versioning is loose; the changelog is authoritative. Releases jump minor numbers for removals. Read the full compare diff and skim DependencyInjection/ for deleted keys each time. Don't let composer update touch this package in CI.
  • Active development, still few public field reports. The 2.1 → 2.2 progression shows the package is maintained (it's clearly extracted from Landingi's internal stack), but published dependents remain near zero. Budget time to read the storage adapter and serializer code, and write your own integration tests around the append/load round-trip.
  • Serialization is where these packages break. Value objects, enums, and dates often need custom normalizers. Test round-tripping of every event type you define, including nested objects, before you rely on replay.
  • Event versioning and upcasting. Decide now how you'll handle schema changes to stored events. If the bundle has no upcaster hook, implement versioning in event payloads ({"_v": 1, ...}) and convert in your projector/aggregate hydration layer.
  • Stream naming and multi-tenancy. Namespace streams explicitly (e.g., tenant:{id}:order:{uuid}). Retrofitting tenant isolation into stream names after data exists is painful.
  • Projection rebuilds are operations, not code. Document the replay procedure and make projectors safe to re-run. Any projector that sends emails or calls payment APIs must be split into a projector (idempotent state) plus a separate reactor driven by an outbox.
  • Debugging: log the stream name, expected version, and correlation ID on every append failure. Concurrency exceptions are otherwise opaque. If the bundle has no logging hooks, wrap the store in a decorator that does.
  • Extension points to look for: a configurable serializer, a pluggable storage adapter, an event bus interface, and middleware/hook support on append. If none of these exist, treat the package as an implementation detail behind your own EventStore interface so you can replace it without touching domain code.
  • Pin and monitor. Pin the version exactly and watch the repository for breaking changes. Prefer a fork you control over an unpinned dependency in a system where event history is the source of truth — you cannot regenerate it.
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.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle