lmc/cqrs-bundle
A PHP CQRS bundle that separates write commands from read queries, giving you clean command and query buses with handlers, middleware support, and a structured way to organize business logic in your application.
Architecture fit — the mismatch is unchanged and now version-confirmed. lmc/cqrs-bundle remains a Symfony bundle, not a Laravel package. Release 2.0.0 reinforces this: the headline changes are Symfony-version support policy (allow Symfony 6, drop 4.4/5.1) and adoption of PHP 8.1 language features in the bundle's own signatures. The value is still concentrated in Bundle registration, DI extension loading, compiler passes, and service tagging — none of which exist in Laravel's container. What is portable is lmc/cqrs (the underlying bus library): handler lookup, middleware, no event sourcing. Laravel adoption means pinning 2.0.0 of the library and discarding the bundle, replacing auto-wiring with a hand-written service provider.
Integration feasibility — moderated by 2.0.0, and the risk has moved rather than disappeared. The PHP constraint is now explicit and, on balance, helpful: requiring PHP 8.1 puts the package at the same floor as Laravel 10, and 8.2/8.3 satisfy a >=8.1 floor. The prior "likely broken on modern PHP" concern is downgraded. The Symfony ceiling is now the primary blocker: 2.0.0 declares support for Symfony 6 but stops there, while Laravel 11 and 12 pull Symfony 7 components. A symfony/*: ^7 transitive requirement in the framework is a hard Composer conflict, and it is the single cheapest thing to test. Effort to wrap the library in a bounded Laravel context remains a few days, not a few hours.
Technical risk — still high, with the shape of the risk revised.
Key questions for the team.
symfony/*: ^7? If yes, this evaluation ends at the Composer dry-run and the answer is "in-house bus."Stack fit. Poor as packaged; workable only as a library-only dependency pinned to ^2.0. Treat lmc/cqrs as a candidate implementation of a bus abstraction we own. Define CommandBus/QueryBus interfaces in the domain layer and bind the vendor implementation behind them — this keeps exit cheap and is the most important structural decision here, more so now that the upstream looks terminal.
Migration path. Per-module refactoring, never big-bang, and orthogonal to framework migration.
composer require lmc/cqrs:^2.0 --dry-run --with-all-dependencies against the real application. Confirm no symfony/*: ^7 collision and no PHP upper-bound surprise. If it fails, stop and close the evaluation — this is the expected outcome on Laravel 11+.lmc/cqrs:^2.0 alone in a branch, register one query handler via a service provider. Target 2.0 signatures explicitly — do not lift handler examples from 1.x material; the method signatures changed.DB::transaction wrapping at the dispatch boundary — the library will not do this for you.Compatibility. Pin 2.0.0 exactly and commit the lockfile diff in the ADR. Validate transitive Symfony constraints against what laravel/framework already requires (HttpFoundation, Console, Finder, Translation, Process) — on Laravel 10 this likely resolves; on Laravel 11/12 it likely does not. Also verify the PHP floor assumption: 2.0.0 requires 8.1, which is satisfied by 8.2/8.3, but no upper bound has been tested upstream and no one has run it there.
Sequencing. Do not start until the abstraction interfaces exist and at least one alternative (Messenger or in-house) is priced. Run the constraint check and the spike in parallel with a written ADR; the decision record is the deliverable, not the working demo. Treat 2.0.0 as a frozen snapshot — no upstream fixes should be assumed.
Maintenance. Assume we maintain it. Budget roughly a day per major PHP or Laravel upgrade to verify or patch, plus the initial fork setup if we vendor it (vcs repository or a composer-patches entry). The 2.0.0 release shows the maintainer's pattern: BC-breaking signature changes on major bumps, then silence. That means our fork is not a one-time copy — it is a commitment to re-do those signature migrations ourselves the next time PHP or Symfony moves. Contrast with Symfony Messenger, where that cost sits with a well-resourced upstream.
Support. No SLA, no commercial backing evident, no dependents to lean on. Escalation path is "open an issue and wait, or fix it ourselves" — and after two years of silence on a 2.0.0, the realistic expectation is the latter. For a system on a revenue path, that risk must be accepted explicitly by the tech lead, not absorbed silently by the implementer.
Scaling. In-process and synchronous by design — fine for request-scoped work, wrong for anything long-running. As load grows the remedy is offloading commands to Laravel queues, which requires writing the async dispatch layer ourselves: effectively reimplementing a slice of Messenger. Plan for that cost to appear later rather than believing the package covers it; 2.0.0 adds nothing here.
Failure modes to instrument from day one.
Ramp-up. Low conceptual cost (CQRS-without-event-sourcing is a small idea) but non-zero and now slightly higher convention cost: engineers must learn the library's handler resolution and middleware semantics, which are undocumented outside the repo, and must disregard the larger body of 1.x material that no longer matches. Estimate half a day per engineer plus the spike's findings, and capture them in the ADR so the second team does not repeat the work.
Bottom line. 2.0.0 clarifies the picture without improving it: PHP 8.1 is a non-issue, but Symfony 6 as the ceiling collides with modern Laravel, and a BC-breaking major followed by two years of silence is abandonment evidence rather than maintenance evidence. The defensible play is unchanged but firmer — own the abstraction, treat the package as a reference implementation, and run the Composer dry-run before any spike. If it collides on Symfony 7, or if the spike cannot move off 1.x examples within an afternoon, implement the bus in-house and close the evaluation.
How can I help you explore Laravel packages today?