PHPackages                             somework/cqrs-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. somework/cqrs-bundle

ActiveSymfony-bundle[Framework](/categories/framework)

somework/cqrs-bundle
====================

CQRS utilities for Symfony Messenger integration.

v0.4.0(1mo ago)09[5 PRs](https://github.com/somework/cqrs/pulls)MITPHPPHP ^8.2CI passing

Since Oct 5Pushed 1mo agoCompare

[ Source](https://github.com/somework/cqrs)[ Packagist](https://packagist.org/packages/somework/cqrs-bundle)[ Docs](https://github.com/somework/cqrs)[ RSS](/packages/somework-cqrs-bundle/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (25)Versions (18)Used By (0)

SomeWork CQRS Bundle
====================

[](#somework-cqrs-bundle)

[![CI](https://github.com/somework/cqrs/actions/workflows/ci.yml/badge.svg)](https://github.com/somework/cqrs/actions/workflows/ci.yml)[![codecov](https://camo.githubusercontent.com/095cdf39244146560a779b721bece14746f193c77974438c01495856b42edc48/68747470733a2f2f636f6465636f762e696f2f67682f736f6d65776f726b2f637172732f6272616e63682f6d61696e2f67726170682f62616467652e737667)](https://codecov.io/gh/somework/cqrs)[![PHPStan Level 8](https://camo.githubusercontent.com/ff3c7f8c8667ce643f47e74532748f673482a5f95d7d4269f925f2eebbe5117e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6c6576656c253230382d627269676874677265656e)](https://phpstan.org/)[![License: MIT](https://camo.githubusercontent.com/08cef40a9105b6526ca22088bc514fbfdbc9aac1ddbf8d4e6c750e3a88a44dca/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d626c75652e737667)](LICENSE)[![Latest Version](https://camo.githubusercontent.com/a6918a913e253bb19c198883ac208fd42ed9cfd2a8451abfd2aaeb2a2cebf621/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f736f6d65776f726b2f637172732d62756e646c65)](https://packagist.org/packages/somework/cqrs-bundle)[![Downloads](https://camo.githubusercontent.com/5aa1f0ac496a79db7202d3a320a45f3455baddaf4b601ba12bb6c11a9203f4f3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f736f6d65776f726b2f637172732d62756e646c65)](https://packagist.org/packages/somework/cqrs-bundle)

A Symfony bundle that wires Command, Query, and Event buses on top of Symfony Messenger. It auto-discovers handlers via PHP attributes, provides a configurable stamp pipeline, and ships with testing utilities and production-grade patterns.

Why this bundle?
----------------

[](#why-this-bundle)

Symfony Messenger is a powerful transport layer, but it leaves CQRS wiring as an exercise for the developer. This bundle fills the gap:

- **Auto-discovery** -- Annotate handlers with `#[AsCommandHandler]`, `#[AsQueryHandler]`, or `#[AsEventHandler]` and they are registered automatically. No YAML tags, no manual wiring.
- **Stamp pipeline** -- A composable `StampDecider` pipeline attaches retry policies, transport routing, serializer stamps, metadata, and dispatch-after-current-bus stamps per message type or per individual message class.
- **Type-safe buses** -- Three dedicated buses (`CommandBus`, `QueryBus`, `EventBus`) with distinct semantics: commands support sync/async dispatch, queries always return a result, events are fire-and-forget with zero-to-many handlers.
- **Testing utilities** -- `FakeCommandBus`, `FakeQueryBus`, `FakeEventBus` with `assertDispatched()`, `assertNotDispatched()`, and callback-based property assertions for fast, isolated unit tests.

### Architecture

[](#architecture)

 ```
flowchart LR
    A[Your Code] --> B[CommandBus / QueryBus / EventBus]
    B --> C[DispatchModeDecider]
    C --> D[StampsDecider Pipeline]
    D --> E1[RetryPolicy]
    D --> E2[Transport]
    D --> E3[Serializer]
    D --> E4[Metadata]
    D --> E5[DispatchAfterCurrentBus]
    D --> F[Symfony Messenger]
    F --> G[Handler]
```

      Loading ### How does it compare?

[](#how-does-it-compare)

CapabilityRaw MessengerCQRS BundleEcotone**Handler discovery**Manual YAML tags or `#[AsMessageHandler]``#[AsCommandHandler]` / `#[AsQueryHandler]` / `#[AsEventHandler]` with auto-discoveryAttribute-based with conventions**Type safety**Single `MessageBusInterface`Separate `CommandBus`, `QueryBus`, `EventBus` with typed dispatch methodsSeparate gateway interfaces**Bus abstraction**You build itThree buses with sync/async routing, `DispatchMode` enumCommand/Query/Event buses built-in**Retry configuration**Per-transport YAML onlyPer-message-class via `RetryPolicy` interface + resolver hierarchyPer-endpoint via attributes**Testing support**`InMemoryTransport``FakeBus` implementations with `assertDispatched()` + callback assertionsTest support module**Async routing**`routing` YAML config`DispatchMode` + `#[Asynchronous]` attribute + per-message transport mappingAsync via polled endpoints**Stamp pipeline**Manual stamp attachmentComposable `StampDecider` pipeline with priority orderingInterceptors (before/after/around)**Event ordering**Not built-in`SequenceAware` interface + `AggregateSequenceStamp`Built-in aggregate versioning**Transactional outbox**Not built-in`OutboxStorage` interface + DBAL implementationBuilt-in with Doctrine**Sagas / Process managers**Not built-inNot built-inBuilt-in saga support**Event sourcing**Not built-inNot built-inBuilt-in event sourcing**OpenTelemetry**Not built-inBridge middleware with trace spansNot built-in**Learning curve**Low (part of Symfony)Low (thin layer over Messenger)Moderate (own conventions)**Dependencies**Symfony onlySymfony MessengerEcotone framework> **Choose raw Messenger** when your app has simple dispatch needs and you want zero additional dependencies. **Choose this bundle** when you want structured CQRS buses, per-message configuration, and testing utilities while staying close to Messenger. **Choose Ecotone** when you need sagas, event sourcing, or a full CQRS/ES framework.

### Feature matrix

[](#feature-matrix)

**Core**

- CommandBus with sync/async dispatch and result extraction
- QueryBus with single-handler validation and typed results
- EventBus with zero-to-many handlers and fire-and-forget semantics
- Attribute-based handler discovery (`#[AsCommandHandler]`, `#[AsQueryHandler]`, `#[AsEventHandler]`)
- Handler interfaces optional -- attributes alone are sufficient

**Stamp Pipeline**

- Composable `StampDecider` system with priority ordering (`@api` -- extend it yourself)
- Per-message retry policies via `RetryPolicy` interface
- Per-message transport routing with `TransportNamesStamp` or `SendMessageToTransportsStamp`
- Per-message serializer stamps
- Per-message metadata stamps with correlation ID support
- `DispatchAfterCurrentBusStamp` control per message

**Patterns**

- Causation ID propagation across nested dispatches
- Idempotency bridge (`IdempotencyStamp` to `DeduplicateStamp`)
- Event ordering with `SequenceAware` and `AggregateSequenceStamp`
- Rate limiting via Symfony Rate Limiter integration
- Transactional outbox with DBAL storage and relay command

**Developer Experience**

- `FakeCommandBus`, `FakeQueryBus`, `FakeEventBus` for unit testing
- `assertDispatched()` / `assertNotDispatched()` with callback-based property assertions
- `somework:cqrs:generate` scaffold command for messages and handlers
- `somework:cqrs:list` handler catalogue
- `somework:cqrs:debug-transports` transport diagnostics
- `somework:cqrs:health-check` for monitoring

**Observability**

- OpenTelemetry bridge middleware (trace spans for dispatch and handling)
- PSR-3 structured logging across buses, deciders, and resolvers

**Integration**

- Symfony Flex recipe for zero-touch installation
- `CommandBusInterface`, `QueryBusInterface`, `EventBusInterface` for DI and testing
- `#[Asynchronous]` attribute for transport routing without YAML config

Installation
------------

[](#installation)

### Requirements

[](#requirements)

- PHP 8.2 or newer.
- Symfony 7.2 or newer.

### With Symfony Flex (recommended)

[](#with-symfony-flex-recommended)

```
composer require somework/cqrs-bundle
```

Flex automatically registers the bundle in `config/bundles.php` and creates a commented `config/packages/somework_cqrs.yaml` with all available options.

### Without Symfony Flex

[](#without-symfony-flex)

Install the bundle via Composer:

```
composer require somework/cqrs-bundle
```

Then register it manually in `config/bundles.php`:

```
return [
    // ...
    SomeWork\CqrsBundle\SomeWorkCqrsBundle::class => ['all' => true],
];
```

Create `config/packages/somework_cqrs.yaml` (see `docs/flex-recipe/` for a template with all available options).

### Verify the installation

[](#verify-the-installation)

Run the bundled console tooling to verify the bundle is registered:

```
bin/console somework:cqrs:list
```

> **Flex Recipe:** The recipe files are in `docs/flex-recipe/` and are pending submission to [symfony/recipes-contrib](https://github.com/symfony/recipes-contrib). Until published, manual bundle registration is required.

Quick start
-----------

[](#quick-start)

### Step 1 -- Define a command message

[](#step-1----define-a-command-message)

```
namespace App\Application\Command;

use SomeWork\CqrsBundle\Contract\Command;

final class CreateTask implements Command
{
    public function __construct(
        public readonly string $id,
        public readonly string $name,
    ) {}
}
```

### Step 2 -- Create the handler

[](#step-2----create-the-handler)

```
namespace App\Application\Command;

use SomeWork\CqrsBundle\Attribute\AsCommandHandler;
use SomeWork\CqrsBundle\Contract\CommandHandler;

#[AsCommandHandler(command: CreateTask::class)]
final class CreateTaskHandler implements CommandHandler
{
    public function __invoke(CreateTask $command): mixed
    {
        // Save task to database...
        return null;
    }
}
```

### Step 3 -- Inject the bus and dispatch

[](#step-3----inject-the-bus-and-dispatch)

```
namespace App\Controller;

use App\Application\Command\CreateTask;
use SomeWork\CqrsBundle\Contract\CommandBusInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;

final class TaskController
{
    #[Route('/tasks', methods: ['POST'])]
    public function create(Request $request, CommandBusInterface $commandBus): JsonResponse
    {
        $data = $request->toArray();

        $commandBus->dispatch(new CreateTask(
            id: uuid_create(),
            name: $data['name'],
        ));

        return new JsonResponse(['status' => 'ok'], 201);
    }
}
```

Documentation
-------------

[](#documentation)

Full documentation is available at **[somework.github.io/cqrs](https://somework.github.io/cqrs/)**.

- [Getting Started](docs/getting-started.md) -- progressive tutorial from install to advanced patterns
- [Usage Guide](docs/usage.md) -- core patterns, dispatch modes, console commands
- [Configuration Reference](docs/reference.md) -- every `somework_cqrs` option explained
- [Testing Guide](docs/testing.md) -- FakeBus, assertions, integration testing
- [Production Guide](docs/production.md) -- deployment, workers, monitoring
- [Troubleshooting](docs/troubleshooting.md) -- common issues and solutions
- [Upgrade Guide](UPGRADE.md) -- migration between versions
- [Changelog](CHANGELOG.md)

### Advanced topics

[](#advanced-topics)

- [Retry Policies](docs/retry.md)
- [Transactional Outbox](docs/outbox.md)
- [Event Ordering](docs/event-ordering.md)
- [Idempotency](docs/idempotency.md)
- [Rate Limiting](docs/rate-limiting.md)

License
-------

[](#license)

MIT. See [LICENSE](LICENSE).

###  Health Score

38

—

LowBetter than 85% of packages

Maintenance89

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~19 days

Recently: every ~41 days

Total

10

Last Release

56d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/19801428?v=4)[Igor Pinchuk](/maintainers/somework)[@somework](https://github.com/somework)

---

Top Contributors

[![somework](https://avatars.githubusercontent.com/u/19801428?v=4)](https://github.com/somework "somework (184 commits)")

---

Tags

symfonycommand busdddevent busMessengercqrsquery-bus

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/somework-cqrs-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/somework-cqrs-bundle/health.svg)](https://phpackages.com/packages/somework-cqrs-bundle)
```

###  Alternatives

[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.3M152](/packages/sulu-sulu)[shopware/platform

The Shopware e-commerce core

3.3k1.5M3](/packages/shopware-platform)[sylius/sylius

E-Commerce platform for PHP, based on Symfony framework.

8.4k5.6M651](/packages/sylius-sylius)[contao/core-bundle

Contao Open Source CMS

1231.6M2.4k](/packages/contao-core-bundle)[prestashop/prestashop

PrestaShop is an Open Source e-commerce platform, committed to providing the best shopping cart experience for both merchants and customers.

9.0k15.4k](/packages/prestashop-prestashop)[shopware/core

Shopware platform is the core for all Shopware ecommerce products.

595.2M386](/packages/shopware-core)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
