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 Phpstan Extension Laravel Package

patchlevel/event-sourcing-phpstan-extension

PHPStan extension for patchlevel/event-sourcing that validates aggregates before runtime: flags uninitialized and unused properties, write-only state, event recording inside apply methods, and state changes outside apply.

View on GitHub
Deep Wiki
Context7

Product Decisions This Supports

  • Build vs. buy for event sourcing infrastructure. This is a zero-code, dev-only add-on to patchlevel/event-sourcing. It doesn't add runtime capability; it converts a class of event sourcing bugs from "caught in review or production" to "caught in CI." If the team is already standardizing on the Patchlevel event sourcing stack, this is effectively a free correctness upgrade — no build cost beyond a config line, no lock-in beyond a dev dependency.
  • Whether to raise static analysis strictness on domain code. The property-initialization rule exists specifically to eliminate PHPStan false positives on aggregate state. Today, teams that want a high PHPStan level on event-sourced code often have to ignore aggregate properties or drop the level. Supporting adoption of this extension is the prerequisite decision for moving those modules to level: max — a quality-gate decision with real ongoing value.
  • Aggregate design standards and review burden. Three of the five rules (unused properties, write-only properties, state writes outside apply) are not lint nits — they encode architectural intent. Write-only state usually belongs in a projection; unwritten state usually means a typo or an unwired event; recording inside apply produces duplicated events on replay. Adopting the extension converts a domain-modeling checklist that reviewers must remember into an automated gate. That reduces the senior-reviewer bottleneck on event sourcing PRs.
  • Engineering roadmap sequencing: event-sourcing migration risk. If the roadmap includes moving a workflow to event sourcing — or has recently — the failure modes this catches (state that vanishes on reload, duplicated events on replay) are the expensive, hard-to-diagnose kind. Adding the extension is a low-cost de-risking step for that initiative, and it pairs naturally with test/story coverage.
  • Dependency and upgrade policy. The rules depend on the library's API surface (apply, recordThat, aggregate base classes). That creates a coupling: library upgrades and extension upgrades should land together, plus a baseline regeneration step. This is a small, recurring process cost that belongs in the upgrade runbook, not a one-time install.
  • Where it does not move the needle. It changes nothing about runtime, performance, or user-facing features. It should not be positioned as a platform investment or a reason to choose the Patchlevel library over alternatives — it's a follow-on nicety that only pays off once the library is already the choice.

When to Consider This Package

Adopt when all of the following hold:

  • You already use patchlevel/event-sourcing in production or in a project committing to it. The extension is inert on any other codebase.
  • You run PHPStan in CI and want event-sourcing correctness to be a blocking check rather than a review convention.
  • You have aggregates (roots or child aggregates) with meaningful apply* methods and invariants — the rules are most valuable where aggregates carry real state.
  • You can absorb a first-run error burst via a baseline and then burn it down. A team that cannot commit to that cleanup will just accumulate ignores.
  • You can keep the extension version paired with the library version and regenerate the baseline on each bump.

Look elsewhere or defer when:

  • No event sourcing at all, or event sourcing via a different library. The rules type-filter on Patchlevel's aggregate base classes; nothing will match.
  • Event sourcing is trivial or absent from the critical path — a handful of tiny aggregates may not justify the version-coupling and baseline maintenance overhead.
  • Heavy magic access to aggregate state (__get, reflection, dynamic property access). The unused/write-only rules rely on PHPStan's read/write analysis and will produce false positives or silences. Prefer typed getters first, or expect to manage identifier-scoped ignores.
  • Very low ecosystem maturity is a concern. One star, zero dependents, single active contributor, and version 1.x milestones with 0–7 PRs each. The code is small and MIT-licensed, which caps the risk, but there is no community redundancy — if patchlevel/event-sourcing changes shape, this repo follows or lags. Pin the version and treat it as a maintained-by-vendor asset whose continuity is tied to the library's.
  • You need it in non-PHPStan toolchains. Nothing to offer Psalm, PHP CS Fixer, or runtime guard rails.

How to Pitch It (Stakeholders)

For executives: This is a small, free, dev-only add-on for teams using the Patchlevel event sourcing library. It turns a category of subtle, expensive bugs — state that silently disappears when an aggregate is reloaded, and duplicate events on replay — from "discovered in production or in a painful debugging session" into "flagged automatically in CI before merge." There is no runtime footprint, no license cost, and no user-facing change. The cost is a config line plus a small ongoing habit of keeping it version-matched with the library. The payoff is fewer senior-reviewer hours spent manually checking event sourcing conventions and a foundation for raising code-quality gates on domain code. Recommended as a default for any team already on this library, and as a deliberate non-decision for teams that aren't.

For engineering: Install as a dev dependency, register extension.neon in every PHPStan config that scans aggregates (app and tests), and run it at a high strictness level — this extension is the thing that unblocks level: max on aggregates by removing false "uninitialized property" reports. It ships five rules: property initialization for roots and child aggregates, unused properties (no apply writes them), write-only properties (written but never read — usually a projection that leaked onto the aggregate), recording inside apply (causes duplicated events on replay), and state writes outside apply (state lost on reload). All rules are configurable.

Three integration realities to plan for. First, a first run on an existing codebase produces a burst of findings — baseline by error identifier, never by message regex, and burn the baseline down incrementally; regenerate (and inspect) it after every upgrade rather than blindly. Second, version-pair with patchlevel/event-sourcing and re-run PHPStan in the same PR as any library bump, because a mismatch can make the rules silently stop matching your aggregates — worse than a loud failure. Third, verify it's actually running by introducing a deliberate violation in a throwaway aggregate and confirming it's flagged; forgetting the includes: line is the most common reason the extension "doesn't work."

Treat the findings as design feedback, not noise. A write-only property wants to be deleted or moved to a read model. An unwritten property wants a typo fixed or a missing event wired up. Structure new aggregates to the pattern the rules enforce — command method validates → records event → framework calls apply → apply only assigns properties — and the rules stop being a checker and start being a design guide. Note the caveat: single contributor, one star, no dependents — pin the version, and treat it as a vendor asset that moves with the library rather than an independent community dependency.

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.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel