diff --git a/.docs/README.md b/.docs/README.md deleted file mode 100644 index c956f4c..0000000 --- a/.docs/README.md +++ /dev/null @@ -1,484 +0,0 @@ -# Contributte Messenger - -## Content - -- [Setup](#setup) -- [Relying](#relying) -- [Configuration](#configuration) -- [Integrations](#integrations) -- [Limitations](#limitations) -- [Examples](#examples) - -## Setup - -```bash -composer require contributte/messenger -``` - -```neon -extensions: - messenger: Contributte\Messenger\DI\MessengerExtension -``` - -## Relying - -Take advantage of empowering this package with extra packages: - -- `symfony/console` via [contributte/console](https://github.com/contributte/console) -- `symfony/event-dispatcher` via [contributte/event-dispatcher](https://github.com/contributte/event-dispatcher) - -### `symfony/console` - -This package relies on `symfony/console`, use prepared [contributte/console](https://github.com/contributte/console) -integration. - -```bash -composer require contributte/console -``` - -```neon -extensions: - console: Contributte\Console\DI\ConsoleExtension(%consoleMode%) -``` - -Since this moment when you type `bin/console`, there will be registered commands from Symfony Messenger. - -![Console Commands](https://raw.githubusercontent.com/contributte/messenger/master/.docs/assets/console.png) - -### `symfony/event-dispatcher` - -This package relies on `symfony/event-dispatcher`, use -prepared [contributte/event-dispatcher](https://github.com/contributte/event-dispatcher) integration. - -```bash -composer require contributte/event-dispatcher -``` - -```neon -extensions: - events: Contributte\EventDispatcher\DI\EventDispatcherExtension -``` - -## Configuration - -> [!TIP] -> See [Symfony Messenger documentation](https://symfony.com/doc/current/messenger.html) for more details. - -### Transports - -| Transport | DSN | Description | -|-----------|-----|-------------| -| [Sync](https://symfony.com/doc/current/messenger.html#transport-configuration) | `sync://` | Handles messages immediately | -| [In-Memory](https://symfony.com/doc/current/messenger.html#in-memory-transport) | `in-memory://` | Stores messages in memory (testing) | -| [Redis](https://symfony.com/doc/current/messenger.html#redis-transport) | `redis://localhost:6379` | Async via Redis streams | -| [Doctrine](https://symfony.com/doc/current/messenger.html#doctrine-transport) | `doctrine://default` | Async via database table | -| [AMQP](https://symfony.com/doc/current/messenger.html#amqp-transport) | `amqp://guest:guest@localhost:5672` | Async via RabbitMQ | - -### Console Commands - -| Command | Description | -|---------|-------------| -| [`messenger:consume`](https://symfony.com/doc/current/messenger.html#consuming-messages-running-the-worker) | Consume messages from transport | -| [`messenger:stop-workers`](https://symfony.com/doc/current/messenger.html#deploying-to-production) | Gracefully stop workers | -| `messenger:setup-transports` | Setup transport infrastructure | -| [`messenger:failed:show`](https://symfony.com/doc/current/messenger.html#saving-retrying-failed-messages) | Show failed messages | -| [`messenger:failed:retry`](https://symfony.com/doc/current/messenger.html#saving-retrying-failed-messages) | Retry failed messages | -| [`messenger:failed:remove`](https://symfony.com/doc/current/messenger.html#saving-retrying-failed-messages) | Remove failed messages | -| `messenger:debug` | Debug routing and handlers | - -Minimal configuration example: - -```neon -messenger: - transport: - sync: - dsn: sync:// - - routing: - App\Domain\SimpleMessage: [sync] - -services: - - App\Domain\SimpleMessageHandler -``` - -Full configuration example: - -```neon -messenger: - # Enable or disable Tracy debug panel - debug: - panel: %debugMode% - - # Worker limits for production environments. - # Workers will gracefully stop when configured limit is reached. - worker: - memoryLimit: 134217728 # int|null (bytes), e.g. 128 MB - timeLimit: 3600 # int|null (seconds), e.g. 1 hour - messageLimit: 1000 # int|null, stop after N messages - failureLimit: 5 # int|null, stop after N failures - - # PSR-6 cache pool for messenger:stop-workers command and worker restart signal. - # When configured, registers StopWorkersCommand and StopWorkerOnRestartSignalListener. - cache: @cache.pool - - # Fallback bus for RoutableMessageBus. Used when no bus stamp is present on the envelope. - # Defaults to null (no fallback). Set to a bus name to enable. - fallbackBus: messageBus - - # Defines buses, default one are messageBus, queryBus and commandBus. - bus: - messageBus: - # To disable default middlewares stack (see https://symfony.com/doc/current/messenger.html#middleware) - defaultMiddlewares: false - - # Define middlewares just for this bus. - middlewares: - #- LoggerMiddleware() - #- @loggerMiddleware - autowired: true - allowNoHandlers: false - allowNoSenders: true - - # Defined class must implement MessageBusInterface - class: App\Model\Bus\MyMessageBus - - # Define wrapper class for easy autowiring between multiple buses (eventBus, messageBus, commandBus, ...) - wrapper: App\Model\Bus\CommandBus - - queryBus: - autowired: false - - # Defines serializers. - serializer: - default: Symfony\Component\Messenger\Transport\Serialization\PhpSerializer - # custom: @customSerializer - - # Defines loggers. - # - httpLogger is used in your presenters/controllers - # - consoleLogger is used in symfony/console commands - logger: - httpLogger: Psr\Log\NullLogger - # httpLogger: @specialLogger - consoleLogger: Symfony\Component\Console\Logger\ConsoleLogger - # consoleLogger: @specialLogger - - # Defines transport factories. - # Built-in factories (sync, inMemory, amqp, redis) are auto-registered when their classes exist. - # Doctrine factory is auto-registered when ConnectionRegistry is available in the container. - transportFactory: - # redis: Symfony\Component\Messenger\Bridge\Redis\Transport\RedisTransportFactory - # sync: Symfony\Component\Messenger\Transport\Sync\SyncTransportFactory - # amqp: Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpTransportFactory - # doctrine: Symfony\Component\Messenger\Bridge\Doctrine\Transport\DoctrineTransportFactory - # inMemory: Symfony\Component\Messenger\Transport\InMemory\InMemoryTransportFactory - # inMemory: @customMemoryTransportFactory - - # Defines global failure transport. Default is none. - # - After retrying, messages will be sent to the "failed" transport. - # - By default if no "failureTransport" is configured inside a transport global will be used. - failureTransport: failed - - # Define transports (async or sync) - transport: - - # Redis (async) transport - redis: - dsn: "redis://localhost?dbIndex=1" - options: [] - serializer: default - failureTransport: db - - # Doctrine (async) transport - db: - dsn: doctrine://postgres:password@localhost:5432 - # to disable retry - retryStrategy: null - - # Sync transport - sync: - dsn: sync:// - - # In memory (sync) transport - memory: - dsn: in-memory:// - serializer: @customSerializer - - # Since no failed transport is configured, the one used will be the global "failureTransport" set - # failureTransport: db - - # Retry configuration. - retryStrategy: - maxRetries: 3 - # milliseconds delay - delay: 1000 - # causes the delay to be higher before each retry - # e.g. 1 second delay, 2 seconds, 4 seconds - multiplier: 2 - maxDelay: 0 - # override all of this with a service that - # implements Symfony\Component\Messenger\Retry\RetryStrategyInterface - # service: @App\RetryStrategy\CustomRetryStrategy - - - # Doctrine (async) transport - failed: - dsn: doctrine://postgres:password@localhost:5432?queue_name=failed - - # Defines routing (message -> transport) - # If the routing for message is missing, the message will be handled by handler immediately when dispatched - # Routing can also be defined via #[AsMessage] attribute on message classes (see below). - # NEON config takes precedence over attributes. - routing: - App\Domain\NewUserEmail: [redis] - App\Domain\ForgotPasswordEmail: [db, redis] - App\Domain\LogText: [db] - - # Route interface - App\Domain\SomeMessageInterface: [db] - - # Route wildcard - *: [sync] - -services: - - App\Domain\LogTextHandler - - App\Domain\NewUserEmailHandler - - App\Domain\ForgotPasswordEmailHandler - - App\RetryStrategy\CustomRetryStrategy -``` - -### Message - -All messages are just simple [POJO](https://stackoverflow.com/questions/41188002/what-does-the-term-plain-old-php-object-popo-exactly-mean). - -```php -text = $text; - } - -} -``` - -#### Routing via `#[AsMessage]` attribute - -Instead of configuring routing in NEON, you can use the `#[AsMessage]` attribute directly on your message class. -The attribute-based routing is auto-discovered from handler method parameters. NEON config takes precedence over attributes. - -```php - -All handlers must also be marked as message handlers to handle messages. -There are 2 different ways to mark your handlers: -1. with the neon tag [`contributte.messenger.handler`]: -```neon -services: - - - class: App\SimpleMessageHandler - tags: - contributte.messenger.handler: # the configuration below is optional - bus: event - alias: simple - method: __invoke - handles: App\SimpleMessage - priority: 0 - from_transport: sync -``` - -2. with the attribute [`#[AsMessageHandler]`] (https://github.com/symfony/messenger/blob/6e749550d539f787023878fad675b744411db003/Attribute/AsMessageHandler.php). -```php - Nettrine -# => order is crucial -# -extensions: - # Common - nettrine.annotations: Nettrine\Annotations\DI\AnnotationsExtension - nettrine.cache: Nettrine\Cache\DI\CacheExtension - nettrine.migrations: Nettrine\Migrations\DI\MigrationsExtension - nettrine.fixtures: Nettrine\Fixtures\DI\FixturesExtension - - # DBAL - nettrine.dbal: Nettrine\DBAL\DI\DbalExtension - nettrine.dbal.console: Nettrine\DBAL\DI\DbalConsoleExtension - - # ORM - nettrine.orm: Nettrine\ORM\DI\OrmExtension - nettrine.orm.cache: Nettrine\ORM\DI\OrmCacheExtension - nettrine.orm.console: Nettrine\ORM\DI\OrmConsoleExtension - nettrine.orm.annotations: Nettrine\ORM\DI\OrmAnnotationsExtension -``` - -## Limitations - -**Roadmap** - -- No Tracy debug panel integration (`TraceableMessageBus`). - -## Examples - -### 1. Manual example - -```sh -composer require contributte/messenger -``` - -```neon -# Extension > Messenger -# -extensions: - messenger: Contributte\Messenger\DI\MessengerExtension - -messenger: - transport: - sync: - dsn: "sync://" - - routing: - App\Domain\LogText: [sync] - -services: - - App\Domain\LogTextHandler -``` - -### 2. Example projects - -We've made a few skeletons with preconfigured Symfony Messenger and Contributte packages. - -- https://github.com/contributte/messenger-skeleton - -### 3. Example playground - -- https://contributte.org/examples.html (more examples) - -## Features - -- Multiple bus types (`messageBus`, `commandBus`, `queryBus`) -- Async transports (Redis, Doctrine, AMQP) -- Retry strategies with exponential backoff -- Failure transport for dead letters -- Handler auto-discovery via `#[AsMessageHandler]` attribute -- Message routing via `#[AsMessage]` attribute -- Interface and wildcard routing -- Worker limits (memory, time, message count) -- Batch handlers support - -## Credits - -This repository is inspired by these packages: - -- [fmasa/messenger](https://github.com/fmasa/messenger) -- [symfony/messenger](https://github.com/symfony/messenger) -- [symfony/redis-messenger](https://github.com/symfony/redis-messenger) - -Thank you folks. diff --git a/README.md b/README.md index 5bfe08e..c436a89 100644 --- a/README.md +++ b/README.md @@ -12,36 +12,508 @@ -

+

Website 🚀 contributte.org | Contact 👨🏻‍💻 f3l1x.io | Twitter 🐦 @contributte

-## Usage +Best Symfony Messenger for Nette framework. It provides multiple bus types, async transports, retry strategies, failure transport, and handler auto-discovery. -To install the latest version of `contributte/messenger` use [Composer](https://getcomposer.org). +## Versions -``` +| State | Version | Branch | Nette | PHP | +|--------|---------|----------|-------|---------| +| dev | `^0.3` | `master` | 3.2+ | `>=8.2` | +| stable | `^0.2` | `master` | 3.2+ | `>=8.2` | + +## Contents + +- [Setup](#setup) +- [Optional integrations](#optional-integrations) +- [Configuration](#configuration) +- [Integrations](#integrations) +- [Limitations](#limitations) +- [Examples](#examples) +- [Features](#features) +- [Credits](#credits) +- [Development](#development) + +## Setup + +```bash composer require contributte/messenger ``` -## Documentation +```neon +extensions: + messenger: Contributte\Messenger\DI\MessengerExtension +``` -For details on how to use this package, check out our [documentation](.docs). +## Optional integrations -## Version +Take advantage of empowering this package with extra packages: -| State | Version | Branch | Nette | PHP | -|--------|---------|----------|-------|---------| -| dev | `^0.3` | `master` | 3.2+ | `>=8.2` | -| stable | `^0.2` | `master` | 3.2+ | `>=8.2` | +- `symfony/console` via [contributte/console](https://github.com/contributte/console) +- `symfony/event-dispatcher` via [contributte/event-dispatcher](https://github.com/contributte/event-dispatcher) + +### `symfony/console` + +This package relies on `symfony/console`, use prepared [contributte/console](https://github.com/contributte/console) +integration. + +```bash +composer require contributte/console +``` + +```neon +extensions: + console: Contributte\Console\DI\ConsoleExtension(%consoleMode%) +``` + +Since this moment when you type `bin/console`, there will be registered commands from Symfony Messenger. + +![Console Commands](.docs/assets/console.png) + +### `symfony/event-dispatcher` + +This package relies on `symfony/event-dispatcher`, use +prepared [contributte/event-dispatcher](https://github.com/contributte/event-dispatcher) integration. + +```bash +composer require contributte/event-dispatcher +``` + +```neon +extensions: + events: Contributte\EventDispatcher\DI\EventDispatcherExtension +``` + +## Configuration + +> [!TIP] +> See [Symfony Messenger documentation](https://symfony.com/doc/current/messenger.html) for more details. + +### Transports + +| Transport | DSN | Description | +|-----------|-----|-------------| +| [Sync](https://symfony.com/doc/current/messenger.html#transport-configuration) | `sync://` | Handles messages immediately | +| [In-Memory](https://symfony.com/doc/current/messenger.html#in-memory-transport) | `in-memory://` | Stores messages in memory (testing) | +| [Redis](https://symfony.com/doc/current/messenger.html#redis-transport) | `redis://localhost:6379` | Async via Redis streams | +| [Doctrine](https://symfony.com/doc/current/messenger.html#doctrine-transport) | `doctrine://default` | Async via database table | +| [AMQP](https://symfony.com/doc/current/messenger.html#amqp-transport) | `amqp://guest:guest@localhost:5672` | Async via RabbitMQ | + +### Console Commands + +| Command | Description | +|---------|-------------| +| [`messenger:consume`](https://symfony.com/doc/current/messenger.html#consuming-messages-running-the-worker) | Consume messages from transport | +| [`messenger:stop-workers`](https://symfony.com/doc/current/messenger.html#deploying-to-production) | Gracefully stop workers | +| `messenger:setup-transports` | Setup transport infrastructure | +| [`messenger:failed:show`](https://symfony.com/doc/current/messenger.html#saving-retrying-failed-messages) | Show failed messages | +| [`messenger:failed:retry`](https://symfony.com/doc/current/messenger.html#saving-retrying-failed-messages) | Retry failed messages | +| [`messenger:failed:remove`](https://symfony.com/doc/current/messenger.html#saving-retrying-failed-messages) | Remove failed messages | +| `messenger:debug` | Debug routing and handlers | + +Minimal configuration example: + +```neon +messenger: + transport: + sync: + dsn: sync:// + + routing: + App\Domain\SimpleMessage: [sync] + +services: + - App\Domain\SimpleMessageHandler +``` + +Full configuration example: + +```neon +messenger: + # Enable or disable Tracy debug panel + debug: + panel: %debugMode% + + # Worker limits for production environments. + # Workers will gracefully stop when configured limit is reached. + worker: + memoryLimit: 134217728 # int|null (bytes), e.g. 128 MB + timeLimit: 3600 # int|null (seconds), e.g. 1 hour + messageLimit: 1000 # int|null, stop after N messages + failureLimit: 5 # int|null, stop after N failures + + # PSR-6 cache pool for messenger:stop-workers command and worker restart signal. + # When configured, registers StopWorkersCommand and StopWorkerOnRestartSignalListener. + cache: @cache.pool + + # Fallback bus for RoutableMessageBus. Used when no bus stamp is present on the envelope. + # Defaults to null (no fallback). Set to a bus name to enable. + fallbackBus: messageBus + + # Defines buses, default one are messageBus, queryBus and commandBus. + bus: + messageBus: + # To disable default middlewares stack (see https://symfony.com/doc/current/messenger.html#middleware) + defaultMiddlewares: false + + # Define middlewares just for this bus. + middlewares: + #- LoggerMiddleware() + #- @loggerMiddleware + autowired: true + allowNoHandlers: false + allowNoSenders: true + + # Defined class must implement MessageBusInterface + class: App\Model\Bus\MyMessageBus + + # Define wrapper class for easy autowiring between multiple buses (eventBus, messageBus, commandBus, ...) + wrapper: App\Model\Bus\CommandBus + + queryBus: + autowired: false + + # Defines serializers. + serializer: + default: Symfony\Component\Messenger\Transport\Serialization\PhpSerializer + # custom: @customSerializer + + # Defines loggers. + # - httpLogger is used in your presenters/controllers + # - consoleLogger is used in symfony/console commands + logger: + httpLogger: Psr\Log\NullLogger + # httpLogger: @specialLogger + consoleLogger: Symfony\Component\Console\Logger\ConsoleLogger + # consoleLogger: @specialLogger + + # Defines transport factories. + # Built-in factories (sync, inMemory, amqp, redis) are auto-registered when their classes exist. + # Doctrine factory is auto-registered when ConnectionRegistry is available in the container. + transportFactory: + # redis: Symfony\Component\Messenger\Bridge\Redis\Transport\RedisTransportFactory + # sync: Symfony\Component\Messenger\Transport\Sync\SyncTransportFactory + # amqp: Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpTransportFactory + # doctrine: Symfony\Component\Messenger\Bridge\Doctrine\Transport\DoctrineTransportFactory + # inMemory: Symfony\Component\Messenger\Transport\InMemory\InMemoryTransportFactory + # inMemory: @customMemoryTransportFactory + + # Defines global failure transport. Default is none. + # - After retrying, messages will be sent to the "failed" transport. + # - By default if no "failureTransport" is configured inside a transport global will be used. + failureTransport: failed + + # Define transports (async or sync) + transport: + + # Redis (async) transport + redis: + dsn: "redis://localhost?dbIndex=1" + options: [] + serializer: default + failureTransport: db + + # Doctrine (async) transport + db: + dsn: doctrine://postgres:password@localhost:5432 + # to disable retry + retryStrategy: null + + # Sync transport + sync: + dsn: sync:// + + # In memory (sync) transport + memory: + dsn: in-memory:// + serializer: @customSerializer + + # Since no failed transport is configured, the one used will be the global "failureTransport" set + # failureTransport: db + + # Retry configuration. + retryStrategy: + maxRetries: 3 + # milliseconds delay + delay: 1000 + # causes the delay to be higher before each retry + # e.g. 1 second delay, 2 seconds, 4 seconds + multiplier: 2 + maxDelay: 0 + # override all of this with a service that + # implements Symfony\Component\Messenger\Retry\RetryStrategyInterface + # service: @App\RetryStrategy\CustomRetryStrategy + + + # Doctrine (async) transport + failed: + dsn: doctrine://postgres:password@localhost:5432?queue_name=failed + + # Defines routing (message -> transport) + # If the routing for message is missing, the message will be handled by handler immediately when dispatched + # Routing can also be defined via #[AsMessage] attribute on message classes (see below). + # NEON config takes precedence over attributes. + routing: + App\Domain\NewUserEmail: [redis] + App\Domain\ForgotPasswordEmail: [db, redis] + App\Domain\LogText: [db] + + # Route interface + App\Domain\SomeMessageInterface: [db] + + # Route wildcard + *: [sync] + +services: + - App\Domain\LogTextHandler + - App\Domain\NewUserEmailHandler + - App\Domain\ForgotPasswordEmailHandler + - App\RetryStrategy\CustomRetryStrategy +``` + +### Message + +All messages are just simple [POJO](https://stackoverflow.com/questions/41188002/what-does-the-term-plain-old-php-object-popo-exactly-mean). + +```php +text = $text; + } + +} +``` + +#### Routing via `#[AsMessage]` attribute + +Instead of configuring routing in NEON, you can use the `#[AsMessage]` attribute directly on your message class. +The attribute-based routing is auto-discovered from handler method parameters. NEON config takes precedence over attributes. + +```php + +All handlers must also be marked as message handlers to handle messages. +There are 2 different ways to mark your handlers: +1. with the neon tag [`contributte.messenger.handler`]: +```neon +services: + - + class: App\SimpleMessageHandler + tags: + contributte.messenger.handler: # the configuration below is optional + bus: event + alias: simple + method: __invoke + handles: App\SimpleMessage + priority: 0 + from_transport: sync +``` + +2. with the attribute [`#[AsMessageHandler]`](https://github.com/symfony/messenger/blob/6e749550d539f787023878fad675b744411db003/Attribute/AsMessageHandler.php). +```php + Nettrine +# => order is crucial +# +extensions: + # Common + nettrine.annotations: Nettrine\Annotations\DI\AnnotationsExtension + nettrine.cache: Nettrine\Cache\DI\CacheExtension + nettrine.migrations: Nettrine\Migrations\DI\MigrationsExtension + nettrine.fixtures: Nettrine\Fixtures\DI\FixturesExtension + + # DBAL + nettrine.dbal: Nettrine\DBAL\DI\DbalExtension + nettrine.dbal.console: Nettrine\DBAL\DI\DbalConsoleExtension + + # ORM + nettrine.orm: Nettrine\ORM\DI\OrmExtension + nettrine.orm.cache: Nettrine\ORM\DI\OrmCacheExtension + nettrine.orm.console: Nettrine\ORM\DI\OrmConsoleExtension + nettrine.orm.annotations: Nettrine\ORM\DI\OrmAnnotationsExtension +``` + +## Limitations + +**Roadmap** + +- No Tracy debug panel integration (`TraceableMessageBus`). + +## Examples + +### 1. Manual example + +```neon +# Extension > Messenger +# +extensions: + messenger: Contributte\Messenger\DI\MessengerExtension + +messenger: + transport: + sync: + dsn: "sync://" + + routing: + App\Domain\LogText: [sync] + +services: + - App\Domain\LogTextHandler +``` + +### 2. Example projects + +We've made a few skeletons with preconfigured Symfony Messenger and Contributte packages. + +- https://github.com/contributte/messenger-skeleton + +### 3. Example playground + +- https://contributte.org/examples.html (more examples) + +## Features + +- Multiple bus types (`messageBus`, `commandBus`, `queryBus`) +- Async transports (Redis, Doctrine, AMQP) +- Retry strategies with exponential backoff +- Failure transport for dead letters +- Handler auto-discovery via `#[AsMessageHandler]` attribute +- Message routing via `#[AsMessage]` attribute +- Interface and wildcard routing +- Worker limits (memory, time, message count) +- Batch handlers support + +## Credits + +This repository is inspired by these packages: + +- [fmasa/messenger](https://github.com/fmasa/messenger) +- [symfony/messenger](https://github.com/symfony/messenger) +- [symfony/redis-messenger](https://github.com/symfony/redis-messenger) + +Thank you folks. ## Development See [how to contribute](https://contributte.org/contributing.html) to this package. -This package is currently maintaining by these authors. +This package is currently maintained by these authors.