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

Cqrs Bundle Laravel Package

somework/cqrs-bundle

A CQRS bundle for Laravel that separates commands from queries with handler registration, buses, and middleware. Structure write and read flows cleanly while keeping integration lightweight and framework-friendly.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle built on Symfony Messenger, not a Laravel package. If you are on Laravel, treat it as a reference implementation of CQRS + outbox patterns rather than something you can drop into app/Providers; the compiler-pass, bundle, and Messenger wiring has no Laravel equivalent. On Symfony, the path is short:

composer require somework/cqrs-bundle:^0.5.1
  1. Register the bundle in config/bundles.php.
  2. Add config/packages/cqrs.yaml and decide up front whether you need the outbox. The outbox is the feature with the most operational consequences and the one this release changed.
  3. Define your first command and handler, dispatch it through the command bus, and confirm it runs synchronously before introducing transports.
  4. Only then configure transports.command_async / transports.event_async and start a worker.

Look first at UPGRADE.md and the bundle's configuration reference, then at the cqrs log channel. If you are coming from 0.5.0 on Symfony 8.2, stop and upgrade to 0.5.1 first — 0.5.0 is not functional there.

The first real use case to validate is an outbox-backed command that writes a row and lets the relay deliver it. That exercises the transaction boundary, the transport routing, and the middleware stack in one go.

Implementation Patterns

Bus-first dispatching. Commands and queries go through the bundle's buses; handlers are registered via attributes and Messenger's handler locator. Keep controllers thin: dispatch a command, return a response. The bundle's middleware (causation id, OpenTelemetry, trace context capture, deduplication lock release) is attached to those buses, so bypassing the bus loses observability and deduplication.

Outbox for at-least-once delivery. Use the outbox when a database write and a message must not diverge. OutboxWriter::store() is the explicit entry point; messages stored through a bus also land in the outbox when it is enabled. The relay then publishes rows to their transport. This is the Symfony analogue of Laravel's DB::afterCommit() dispatch pattern, but with persistence and replay built in.

Transport routing is configuration, not code. transports.command_async and transports.event_async decide where outbox rows go. As of 0.5.1 this also applies when there is no async bus — previously those rows had no transport, were handled in the relay process, and silently ignored DelayStamp. If you want the old in-relay behavior deliberately, point the transport at sync://.

Attribute-driven routing. #[AsMessageHandler(transport: …)], #[Asynchronous], and #[Outbox] are honored; 0.5.1 fixes their routing on Symfony 8.2. Prefer explicit transport: over relying on the default async transport.

Middleware extension. Add custom middleware through the bus configuration. Because the bundle's own passes now run at priority −24 (after Messenger's MessengerPass), your middleware sees a fully built bus, including handler locators and routing.

Testing. Point transports at sync:// or in-memory transports in the test environment and assert on handler effects rather than on relay timing. For outbox tests, assert the row is written in the same transaction as the domain change, then invoke the relay explicitly.

Gotchas and Tips

  • Do not run 0.5.0 on Symfony 8.2. The bundle's compiler passes ran before Messenger's MessengerPass moved to priority −16, so CQRS middleware was missing from buses, handlerless events threw NoHandlerForMessageException, EnvelopeAware handlers failed with "Messenger envelope has not been set", the container failed to compile with the outbox enabled, and #[AsMessageHandler(transport: …)] routes were ignored. Upgrade to 0.5.1 before the Symfony 8.2 upgrade, not after.
  • Compiler-pass priority is now load-bearing. The bundle's passes run at −24 and the cqrs log channel pass at −25. Any application pass that must observe or mutate the bundle's middleware on a bus needs a priority below −25, or it will run too early.
  • A LogicException at build time is diagnostic, not a regression. It means MessengerPass had not built the buses when the bundle's passes needed them. Check for a competing pass ordering or a removed Messenger bundle.
  • 0.5.0 outbox rows keep an empty transport. They are not backfilled. Replay or re-store them if they need to reach a configured transport; otherwise they will continue to be handled by the relay process.
  • Outbox-only apps changed behavior. Newly stored messages now go to transports.command_async / transports.event_async, so their handlers run in that transport's worker. You must run messenger:consume <transport> for it, or set the transport to sync:// to retain relay-process handling.
  • Doctrine transaction middleware is skipped in the store path. With the outbox enabled, DoctrineBridge 8.2's DoctrineDbalTransactionMiddleware and DoctrineDbalOpenTransactionLoggerMiddleware are skipped regardless of service id — they run in the relay instead. Do not assume they wrap the bus dispatch.
  • Debugging. Use the cqrs log channel for bundle-level traces, debug:messenger to inspect buses and handler wiring, and messenger:consume output to confirm which transport a row reached. Missing middleware usually shows up as absent causation ids or trace context, not as an exception.
  • Version pin deliberately. Use ^0.5.1; the release has no API or configuration breaking changes, but the pass-priority behavior and outbox transport routing are operationally significant even though they are "fixed" rather than "changed".
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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect