patchlevel/event-sourcing
A fully featured, developer-friendly event sourcing library for PHP built on Doctrine DBAL. Includes automatic snapshots, stream splitting, versioned subscriptions, crypto-shredding for personal data, event upcasting, and CLI commands.
Architecture fit
patchlevel/event-sourcing is a full-stack event sourcing library for Laravel (aggregates, event store, projectors, reactors, catch-up subscriptions, snapshots, upcasting, encryption). It is opinionated but not framework-invasive: it ships a service provider, artisan commands, and Doctrine DBAL-backed persistence, which means it slots into a Laravel service as the write model while leaving read models to Eloquent/query builders. Release 3.22.0 added header aliases — a change to how event metadata headers are resolved when messages are read. Headers are the library's transport metadata (event type, aggregate id, aggregate version, correlation/causation ids, custom metadata), so this is a change on the hot deserialization path rather than a peripheral utility. Release 3.22.1 is a one-PR patch that fixes cache keys of the metadata factories — i.e. it lands in the same metadata/header layer as 3.22.0's aliases, not in an unrelated corner of the library.
Integration feasibility
Low-to-moderate. Adoption requires a deliberate architectural decision (event-sourced write model, eventual consistency for read models, subscription workers), not just a composer require. The 3.22.0 delta itself is small: one PR, one contributor, semver-minor; header aliases plausibly reduce bespoke MessageDecorator/EventUpcaster code for teams that store events under legacy or third-party header names. 3.22.1 is smaller still — a single bugfix PR with zero resolved issues — and is best treated as the baseline patch for a 3.22 adoption rather than as a separate decision. If we adopt header aliases at all, we should adopt them on 3.22.1, because the patch addresses keying of cached metadata, which is precisely the mechanism that would otherwise serve a stale or wrongly-keyed header set.
Technical risk Overall risk remains low but non-zero on the read path, and 3.22.1 marginally reduces it. A minor feature release with a single contributor and a single PR has a small blast radius, yet header resolution failures are silent by nature: a mis-aliased header can surface as a wrong aggregate version, an unmatched event type, or a replay that terminates mid-stream rather than as an obvious exception at deploy time. A metadata-factory cache key defect is a textbook instance of that class — if two factories (or two configurations, including alias configs) collide on a key, the failure appears as mis-resolved metadata during read/replay rather than as an error at boot. The patch is a correctness fix on that path, so the residual unknowns are now: whether the fix changes on-disk or on-wire formats (it should not — cache keys are internal), and whether cached metadata persists across deploys in a shared Laravel cache store, which would make alias-config changes require explicit invalidation.
Key questions
Message/Headers public API changed, or is this purely additive config?Stack fit Requires PHP 8.2+ and a supported Laravel major (v3 line targets Laravel 10/11), plus MySQL/Postgres/MariaDB/SQLite via Doctrine DBAL. Fits a modular monolith or a bounded-context service where one aggregate family justifies event sourcing; overkill for CRUD-centric domains. Header aliases improve fit specifically where our event store is shared with, or migrated from, another producer that used different header names.
Migration path
Pin to ^3.22.1 rather than ^3.22.0 — there is no reason to land 3.22.0's alias feature without the cache-key fix that accompanies it. Treat the upgrade as a routine patch: composer update patchlevel/event-sourcing, run the suite, exercise a full replay in staging, and clear the application cache as part of the deploy. No data migration is expected if aliases are read-side only and metadata caches are derived rather than authoritative. If aliases normalize on write, adopt expand/contract: keep canonical header names as the source of truth, register aliases only for legacy reads, and do not rename stored headers in place.
Compatibility Semver-patch with no deprecations signalled; backward compatibility is implied but should be confirmed against our pinned minor. Three compatibility edges matter for us: (1) event payload/header schema compatibility with events already persisted by earlier 3.x versions, (2) cross-service compatibility if more than one deployable reads the same event store, where consumers on older minors may not understand aliased headers, and (3) now, cache compatibility — if any metadata cache is shared across deployables, a mixed 3.22.0/3.22.1 fleet could read entries keyed by the old scheme.
Sequencing
Maintenance Low incremental burden: no new infrastructure, no new datastore, no new scheduled job. Ongoing cost is the library upgrade cadence itself (frequent small minors and patches) plus keeping the alias configuration map curated and documented — unmanaged alias config becomes tribal knowledge. 3.22.1 adds one small operational obligation: knowing where metadata caches live and clearing them when header/alias configuration changes.
Support Support load concentrates on replay/subscription incidents rather than day-to-day operation. Teams need runbook coverage for "subscription stalled" and "event failed to deserialize," since header resolution errors present as those symptoms. A single-contributor release remains a mild bus-factor signal for upstream responsiveness, though the fact that a metadata-cache correctness bug was found and patched within the same minor is mildly reassuring on responsiveness.
Scaling Negligible impact. Alias lookup is an in-process map resolution per message read; the cache-key fix changes how cached metadata is keyed, not how much is cached or how fast the store is. It does not change storage layout, index strategy, or subscription throughput in any meaningful way. Scaling remains governed by the existing constraints: DBAL event store write throughput, snapshot policy for long-lived aggregates, and worker concurrency for subscriptions.
Failure modes
Ramp-up Low for the feature, moderate for the domain. Header aliases take minutes to learn; event sourcing itself (aggregate design, projector idempotency, upcasting, replay discipline, cache invalidation discipline) is the real onboarding cost and should be scoped as such. Recommended: adopt on the next service that genuinely needs event sourcing, use 3.22.1 as the baseline version, validate alias semantics during the first staging replay, and document the metadata-cache clear step alongside the existing replay runbook rather than treating either release as a driver for migration.
How can I help you explore Laravel packages today?