jooservices/laravel-events
Laravel package for adding event management to your app, with helpers for creating, scheduling, and displaying events. Lightweight and easy to integrate, it offers a simple foundation for event listings, calendars, and related workflows.
Architecture fit. The package drops into a Laravel service as the write-side of an event/audit log persisted to MongoDB, layered on top of the existing Eloquent/SQL domain. v4.0.0 is a maturity signal rather than a feature release: EventPersisterInterface for subscriber DIP, optional PSR-20 ClockInterface and PSR-3 LoggerInterface injection into EventService, a QueryGuard/QueryExecutor split, and dedicated InvalidEventDataException / InvalidQueryException / InvalidConfigurationException types. For a TPM this maps cleanly to "we can test it, we can time-travel it, we can reason about its failure surface" — three things that were previously implicit. The dual-path correlation queries and byEventClass() / byEventId() filters close real gaps in query ergonomics; ShouldDispatchAfterCommit guidance is the single most important architectural note, because it tells you the package now has a documented stance on SQL-transaction/event-log divergence.
Integration feasibility. Feasible but not free. Four things gate the estimate:
EventLogAction and EventCategory becoming string-backed means every call site that compared, echoed, or persisted EventLogAction::UPDATED as a string needs ->value. This is a mechanical but wide-reaching sweep — cost scales with the number of log-emitting services, not the number of repos.final classes. EventService, subscribers, query services, serializer, DiffHelper, and the console command are now closed. Any team that subclassed EventService for test seams or local overrides must migrate to EventPersisterInterface (or composition). This is the highest-variance item in the upgrade; it is invisible until you run the suite.event_id, event_name, top-level correlation/causation) and php artisan events:install-indexes must be re-run. Sparse-unique on event_id will fail to build if existing documents contain duplicate or null-ish event_id values — treat this as a data-quality gate, not a deploy step.jooservices/dto ^3.2, jooservices/exceptions ^4.0, psr/clock ^1.0, psr/log ^3.0). psr/log ^3.0 is the notable one: it excludes any host library still pinned to psr/log ^1.0/^2.0, and EventSerializerInterface::ensureEnvelope() is a new required method for anyone with a custom serializer.Technical risk. Medium, concentrated in three places. First, the release note says "Treat upgrades from 1.x as a breaking change" inside a 4.0.0 changelog — that implies 2.x/3.x were skipped, yanked, or internal, and we cannot infer our own upgrade path from the repo alone. Second, final + EventSerializerInterface changes break at runtime and test time, not at composer time, so a green composer update is not evidence of a working upgrade. Third, the index change is the only item that can corrupt or block a production data path, and it is the least testable in staging (index builds depend on production data shape).
Key questions.
UPGRADE-4.0.md cover 2.x/3.x intermediate breaks?EventService, subscribers, or DiffHelper, and how many call sites compare enum cases as strings? (Grep-able; get a number before committing to a date.)event_id? What is the duplicate/null rate today?psr/log ^3.0 and jooservices/dto ^3.2 without conflict — and does anything else in the monorepo pin psr/log lower?ShouldDispatchAfterCommit adoption is in-scope, not optional.Stack fit. Requires PHP 8.5-era tooling (the shipped Dockerfile/docker-compose targets PHP 8.5 + MongoDB), Laravel 12/13, and MongoDB as a hard dependency for persistence. Assumption to validate: PHP 8.5 is the dev container baseline, not a raised composer.json floor — confirm before planning a PHP upgrade off the back of this. The package is a natural fit where we already have MongoDB for read models or audit; it is a poor fit for a service that would need to introduce MongoDB solely for this package, since we would acquire a second datastore plus index lifecycle tooling for an event log.
Migration path. Do not attempt a big-bang. Recommended path:
extends on now-final classes. Produce two lists: enum call sites and subclass sites. This is the estimate.^4.0 and force a lock resolution against psr/log ^3.0 and jooservices/*. If it does not resolve, stop — the rest is moot.->value, replace subclass seams with EventPersisterInterface mocks, implement ensureEnvelope() on any custom serializer. No functional changes in this commit.event_id index on a restored copy; measure duplicates/nulls and build time. Decide remediation (backfill, dedupe, partial index) before touching prod.events:install-indexes in a controlled window. The new query paths (byEventName, byEventClass, byEventId) should tolerate the old index set long enough to decouple the two steps — verify this, because if they don't, code and index must ship together and you lose the rollback seam.ShouldDispatchAfterCommit, injected ClockInterface, and context_provider class-strings are follow-on workstreams, not part of the upgrade.Compatibility. Two hard breaks to plan around: psr/log ^3.0 (host-wide dependency surface) and the removed EventDiffData / EventMetadataData bag DTOs — any code constructing or type-hinting those must move to record DTOs' diff / meta / metadata arrays. Note that the package's public surface is now deliberately narrow: models and EventsServiceProvider stay open, everything else is final. Budget for that being a permanent constraint on our extension patterns, not a one-time fix.
Sequencing. Dependency resolution → enum/subclass refactor → serializer contract → index rehearsal → prod code deploy → prod index install → optional adoptions. Roughly three tracks that can run in parallel (refactor, index rehearsal, dependency spike) with a single serialized cutover at the end. Recommend a feature flag or a config-toggled dual-write only if the event log is on a request-critical path; if it is fire-and-forget audit, plain rolling deploy is sufficient.
Maintenance. Net positive. PHPStan max with strict and PHPUnit rules, PHPMD cleancode, Pint PER-CS 3.0, CaptainHook commit conventions, and a CI baseline (PR gate, commitlint, CodeQL, workflow audit) mean the package is now statically verified and its releases are gated — which lowers the odds of the next upgrade being another archaeology exercise. The cost is inherited convention: our PRs touching this integration will need to satisfy stricter static analysis, and our own test suite must mock EventPersisterInterface rather than EventService. Small, one-time, and it improves our tests.
Support. The final-by-default posture is a supportability win — fewer supported extension points, fewer ways to break on upgrade. But it transfers responsibility to us: any behavior we need that the package does not expose via interface or config becomes a fork. The new InvalidConfigurationException on non-callable context_provider is exactly the kind of fail-fast we want; conversely, a silently misconfigured context_provider was previously a plausible production failure mode, so audit our config on upgrade. Docker/compose/Makefile artifacts are dev-experience, not production runtime — do not read them as a deployment recommendation.
Scaling. Query-side work is the substance here: dual-path correlation queries plus byEventName() now correctly filtering event_name, and allowlisted filters with inverted between() rejected, meaning query cost is bounded and malformed ranges fail fast instead of scanning. The new top-level correlation/causation indexes are what make correlation lookups viable at volume — this release is likely a meaningful read-latency improvement for us, not just a cleanup. Write-side, recordManyStoredEvents() now always runs ensureEnvelope(), removing a class of partially-shaped bulk documents. Two scaling caveats: sparse-unique event_id adds write-time uniqueness enforcement (contention and error paths on duplicate IDs — confirm the exception surfaces as InvalidEventDataException and is handled), and MongoDB growth remains unaddressed by this package; retention/TTL is our problem.
Failure modes. Ranked by blast radius:
event_id — production write path degraded during the window. Mitigation: snapshot rehearsal, dedupe beforehand, off-peak execution, documented rollback (drop index).ShouldDispatchAfterCommit) but does not apply it for us — pre-existing bug, now actionable.events:* command surface.psr/log ^3.0 conflict discovered late, blocking the whole upgrade. Mitigation: dependency spike on day one.user_id no longer clobbers context) — if we had a workaround for this in our code, remove it, or we will double-handle.changed payloads.Ramp-up. Low-to-moderate for a team already on Laravel + MongoDB; the concepts (event log, subscribers, correlation IDs) are standard. The real ramp-up cost is the enum and final refactor, which requires someone who knows every call site — plan on one engineer owning the inventory and the mechanical sweep, with the index rehearsal owned separately by whoever holds the MongoDB production access. Estimate the upgrade in days of refactor plus one controlled migration window, not in story points, and gate the schedule on the dependency spike and the index rehearsal both clearing. If either fails, the release is not adoptable on our current timeline and we should stay pinned and revisit — the correctness fixes are worth having, but not worth a broken write path.
How can I help you explore Laravel packages today?