PHPackages                             nepada/message-bus - 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. nepada/message-bus

ActiveLibrary

nepada/message-bus
==================

Opinionated message bus built on top of symfony/messenger.

v3.0.3(5mo ago)431.9k3BSD-3-ClausePHPPHP &gt;=8.2.0 &lt;8.6CI passing

Since Jul 21Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/nepada/message-bus)[ Packagist](https://packagist.org/packages/nepada/message-bus)[ RSS](/packages/nepada-message-bus/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (13)Versions (14)Used By (3)

Message Bus
===========

[](#message-bus)

[![Build Status](https://github.com/nepada/message-bus/workflows/CI/badge.svg)](https://github.com/nepada/message-bus/actions?query=workflow%3ACI+branch%3Amaster)[![Coverage Status](https://camo.githubusercontent.com/fc087e4d503c546b6f0e1d8af1445b7de10f0bb6c4c6e5fd4dae6a813f43a0b3/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6769746875622f6e65706164612f6d6573736167652d6275732f62616467652e7376673f6272616e63683d6d6173746572)](https://coveralls.io/github/nepada/message-bus?branch=master)[![Downloads this Month](https://camo.githubusercontent.com/9e1ea77af52da2fba33baf8f5053971d44caad4778af92869213a960c66dd0b7/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f646d2f6e65706164612f6d6573736167652d6275732e737667)](https://packagist.org/packages/nepada/message-bus)[![Latest stable](https://camo.githubusercontent.com/f82367c993ac1c83e73ca745b9e10b3bf2464f49b40fef0d73575f3f5f723772/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6e65706164612f6d6573736167652d6275732e737667)](https://packagist.org/packages/nepada/message-bus)

Opinionated message bus built on top of [symfony/messenger](https://github.com/symfony/messenger) largely based on the ideas and code base of damejidlo/message-bus, originally developed by [Ondřej Bouda](mailto:ondrej.bouda@gmail.com).

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

[](#installation)

Via Composer:

```
$ composer require nepada/message-bus
```

Conventions
-----------

[](#conventions)

We define two types of messages and corresponding message buses - commands and events.

### Commands

[](#commands)

Command implementation must adhere to these rules:

- class must implement `Nepada\Commands\Command` interface
- class must be named `Command`
- class must be final
- class must be readonly
- command name should be in imperative form ("do something")
- command must be a simple immutable DTO
- command must not contain entities, only references (i.e. `int $orderId`, not `Order $order`)

Examples of good command class names:

- `RejectOrderCommand`
- `CreateUserCommand`

Command handler implementation must adhere to these rules:

- class must implement `Nepada\Commands\CommandHandler` interface
- class must be named `Handler`
- class must be final
- class must implement method named `__invoke`
- `__invoke` method must have exactly one parameter named `$command`
- `__invoke` method parameter must be typehinted with specific command class
- `__invoke` method return type must be `void`
- `__invoke` method must be annotated with `@throws` tags if specific exceptions can be thrown

Example:

```
final class DoSomethingHandler implements \Nepada\MessageBus\Commands\CommandHandler
{
    /**
     * @throws SomeException
     */
    public function __invoke(DoSomethingCommand $command): void
    {
        // ...
    }
}
```

Every command must have exactly one handler.

### Events

[](#events)

Events must be dispatched during command handling only.

Event implementation must adhere to these rules:

- class must implement `Nepada\Events\Event` interface
- class must be named `Event`
- class must be final
- class must be readonly
- event name should be in past tense ("something happened")
- event must be a simple immutable DTO
- event must not contain entities, only references (i.e. `int $orderId`, not `Order $order`)

Examples of good event class names:

- `OrderRejectedEvent`
- `UserRegisteredEvent`

Event subscriber implementation must adhere to these rules:

- class must implement `Nepada\Events\EventSubscriber` interface
- class must be named `On`
- class must be final
- class must implement method named `__invoke`
- `__invoke` method must have exactly one parameter named `$event`
- `__invoke` method parameter must be typehinted with specific event class
- `__invoke` method return type must be `void`
- `__invoke` method must be annotated with `@throws` tags if specific exceptions can be thrown

Example:

```
final class DoSomethingOnSomethingHappened implements \Nepada\MessageBus\Events\EventSubscriber
{
     public function __invoke(SomethingHappenedEvent $event): void {}
}
```

Every event may have any number of subscribers, or none at all.

Configuration &amp; Usage
-------------------------

[](#configuration--usage)

### Enforcing conventions by static analysis

[](#enforcing-conventions-by-static-analysis)

Most of the conventions described above may be enforced by static analysis. The analysis should be run during the compilation of DI container, triggering it at application runtime is not recommended.

```
use Nepada\MessageBus\StaticAnalysis\ConfigurableHandlerValidator;
use Nepada\MessageBus\StaticAnalysis\HandlerType;
use Nepada\MessageBus\StaticAnalysis\MessageHandlerValidationConfiguration;

// Validate command handler
$commandHandlerType = HandlerType::fromString(DoSomethingHandler::class);
$commandHandlerConfiguration = MessageHandlerValidationConfiguration::command();
$commandHandlerValidator = new ConfigurableHandlerValidator($commandHandlerConfiguration);
$commandHandlerValidator->validate($commandHandlerType);

// Validate event subscriber
$eventSubscriberType = HandlerType::fromString(DoSomethingOnSomethingHappened::class);
$eventSubscriberConfiguration = MessageHandlerValidationConfiguration::event();
$eventSubscriberValidator = new ConfigurableHandlerValidator($eventSubscriberConfiguration);
$eventSubscriberValidator->validate($eventSubscriberType);
```

#### Bleeding edge (new rules)

[](#bleeding-edge-new-rules)

To maintain backwards compatibility new rules are not enforced by default. They can be enabled by passing `$bleedingEdge` flag when creating validator configuration, e.g. `MessageHandlerValidationConfiguration::command(true)`.

These rules will be enabled in the next major version: (none at the moment)

### Extracting handled message type from handler class

[](#extracting-handled-message-type-from-handler-class)

Use `MessageTypeExtractor` to retrieve the message type that a given command handler or event subscriber handles:

```
use Nepada\MessageBus\StaticAnalysis\HandlerType;
use Nepada\MessageBus\StaticAnalysis\MessageTypeExtractor;

// Extracting handled message type
$messageTypeExtractor = new MessageTypeExtractor();
$commandHandlerType = HandlerType::fromString(DoSomethingHandler::class);
$messageTypeExtractor->extract($commandHandlerType); // MessageType instance for DoSomethingCommand
```

### Logging

[](#logging)

`LoggingMiddleware` implements logging into standard PSR-3 logger. Start of message handling and its success or failure are logged separately. Logging context is filled with the extracted attributes of command or event DTO.

### Nested message handling

[](#nested-message-handling)

Generally, it's not a good idea to execute commands from within another command handler. You can completely forbid this behavior with `PreventNestedHandlingMiddleware`.

### Configuration

[](#configuration)

It is completely up to you to use the provided building blocks together with Symfony Messenger and configure one or more instances of command and/or event buses.

A minimal setup in pure PHP might look something like this:

```
use Nepada\MessageBus\Commands\CommandHandlerLocator;
use Nepada\MessageBus\Commands\MessengerCommandBus;
use Nepada\MessageBus\Events\EventSubscribersLocator;
use Nepada\MessageBus\Events\MessengerEventDispatcher;
use Nepada\MessageBus\Logging\LogMessageResolver;
use Nepada\MessageBus\Logging\MessageContextResolver;
use Nepada\MessageBus\Logging\PrivateClassPropertiesExtractor;
use Nepada\MessageBus\Middleware\LoggingMiddleware;
use Nepada\MessageBus\Middleware\PreventNestedHandlingMiddleware;
use Symfony\Component\Messenger\MessageBus;
use Symfony\Component\Messenger\Middleware\DispatchAfterCurrentBusMiddleware;
use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware;

$dispatchAfterCurrentBusMiddleware = new DispatchAfterCurrentBusMiddleware();
$preventNestedHandlingMiddleware = new PreventNestedHandlingMiddleware();
$loggingMiddleware = new LoggingMiddleware(
    new LogMessageResolver(),
    new MessageContextResolver(
        new PrivateClassPropertiesExtractor(),
    ),
    $psrLogger,
);
$handleCommandMiddleware = new HandleMessageMiddleware(
    new CommandHandlerLocator(
        $psrContainer,
        [
            DoSomethingCommand::class => 'doSomethingHandlerServiceName',
        ],
    ),
);
$handleEventMiddleware = new HandleMessageMiddleware(
    new EventSubscribersLocator(
        $psrContainer,
        [
            SomethingHappenedEvent::class => [
                'doSomethingOnSomethingHappenedServiceName',
                'doSomethingElseOnSomethingHappenedServiceName',
            ],
        ],
    ),
);
$eventDispatcher = new MessengerEventDispatcher(
    new MessageBus([
        $dispatchAfterCurrentBusMiddleware,
        $loggingMiddleware,
        $handleEventMiddleware,
    ]),
);
$commandBus = new MessengerCommandBus(
    new MessageBus([
        $dispatchAfterCurrentBusMiddleware,
        $loggingMiddleware,
        $preventNestedHandlingMiddleware,
        $handleCommandMiddleware,
    ]),
);
```

Note the usage of `DispatchAfterCurrentBusMiddleware` - this is necessary to ensure that events produced during the handling of a command are handled only after the command handling **successfully** finishes.

For Nette Framework integration, consider using [nepada/message-bus-nette](https://github.com/nepada/message-bus-nette).

### Extensions

[](#extensions)

- [nepada/message-bus-doctrine](https://github.com/nepada/message-bus-doctrine) Doctrine ORM integration - transaction handling, collecting and emitting domain events from entities, etc.
- [nepada/message-bus-nette](https://github.com/nepada/message-bus-nette) Nette Framework DI extension.
- [nepada/phpstan-message-bus](https://github.com/nepada/phpstan-message-bus) adding support for propagating checked exceptions thrown out of command handlers up to the command bus caller

Credits
-------

[](#credits)

Static analysis part of the code base and a lot of other core ideas are borrowed from damejidlo/message-bus, originally developed by [Ondřej Bouda](mailto:ondrej.bouda@gmail.com).

###  Health Score

56

—

FairBetter than 98% of packages

Maintenance82

Actively maintained with recent releases

Popularity31

Limited adoption so far

Community14

Small or concentrated contributor base

Maturity80

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 58.2% 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 ~218 days

Recently: every ~192 days

Total

10

Last Release

162d ago

Major Versions

v2.2.0 → v3.0.02024-04-01

PHP version history (7 changes)v2.0.0PHP &gt;=7.4.0

v2.1.1PHP &gt;=7.4.0 &lt;8.2

v2.1.2PHP &gt;=7.4.0 &lt;8.3

v2.2.0PHP &gt;=8.1.0 &lt;8.4

v3.0.0PHP &gt;=8.2.0 &lt;8.4

v3.0.1PHP &gt;=8.2.0 &lt;8.5

v3.0.2PHP &gt;=8.2.0 &lt;8.6

### Community

Maintainers

![](https://www.gravatar.com/avatar/5b4780fe328102c4572737db639653c29d3081d1d3e051467f00d7f09a776399?d=identicon)[xificurk](/maintainers/xificurk)

---

Top Contributors

[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (131 commits)")[![xificurk](https://avatars.githubusercontent.com/u/117465?v=4)](https://github.com/xificurk "xificurk (94 commits)")

---

Tags

Messengermessage bus

###  Code Quality

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/nepada-message-bus/health.svg)

```
[![Health](https://phpackages.com/badges/nepada-message-bus/health.svg)](https://phpackages.com/packages/nepada-message-bus)
```

###  Alternatives

[sulu/sulu

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

1.3k1.3M152](/packages/sulu-sulu)[danog/madelineproto

Async PHP client API for the telegram MTProto protocol.

3.4k855.0k18](/packages/danog-madelineproto)[contao/core-bundle

Contao Open Source CMS

1231.6M2.4k](/packages/contao-core-bundle)[koco/messenger-kafka

Symfony Messenger Kafka Transport

931.1M1](/packages/koco-messenger-kafka)[jwage/phpamqplib-messenger

Symfony messenger transport for the php-amqplib/php-amqplib library.

84149.7k1](/packages/jwage-phpamqplib-messenger)

PHPackages © 2026

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