{"id":26581605,"url":"https://github.com/awd-studio/service-buses","last_synced_at":"2025-10-24T12:21:07.771Z","repository":{"id":62490257,"uuid":"193806372","full_name":"awd-studio/service-buses","owner":"awd-studio","description":"A simple library, to implement CQRS pattern with PHP projects.","archived":false,"fork":false,"pushed_at":"2023-08-10T16:02:18.000Z","size":168,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-10-13T14:45:02.070Z","etag":null,"topics":["command-bus","cqrs","cqrs-pattern","event-bus","php","query-bus","service-buses"],"latest_commit_sha":null,"homepage":"https://github.com/awd-studio/service-buses","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/awd-studio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-06-26T01:16:53.000Z","updated_at":"2023-07-04T16:46:05.000Z","dependencies_parsed_at":"2022-11-02T11:02:24.944Z","dependency_job_id":null,"html_url":"https://github.com/awd-studio/service-buses","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awd-studio%2Fservice-buses","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awd-studio%2Fservice-buses/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awd-studio%2Fservice-buses/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awd-studio%2Fservice-buses/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/awd-studio","download_url":"https://codeload.github.com/awd-studio/service-buses/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245071358,"owners_count":20556299,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["command-bus","cqrs","cqrs-pattern","event-bus","php","query-bus","service-buses"],"created_at":"2025-03-23T07:30:45.073Z","updated_at":"2025-10-24T12:21:07.689Z","avatar_url":"https://github.com/awd-studio.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Service buses in PHP\n\n## A simple library, to implement `CQRS`-ish pattern on PHP projects.\n\n![Build status](https://github.com/awd-studio/service-buses/actions/workflows/tests.yml/badge.svg)\n[![Coverage Status](https://coveralls.io/repos/github/awd-studio/service-buses/badge.svg?branch=master)](https://coveralls.io/github/awd-studio/service-buses?branch=master)\n\n#### Features:\n- Neither messages nor handlers don't need to extend or implement any additional abstraction.\n- A handler can be any of `callable` items.\n- Handlers can subscribe on any of parents or implementations of an event.\n- Contains a decorator to register handles as services handled via `PSR-11`'s container.\n- Contains a decorator to auto-subscribe handlers by a typehint on a message that it handles.\n- Provides ready to go bus patterns such a `Command Bus`, a `Query Bus` and an `Event Bus` implementations.\n\n#### Contents:\n- [Get started](#get-started)\n- [Handling messages](#handling-messages)\n- [Predefined buses](#predefined-buses)\n  - [Command Bus](#command-bus)\n  - [Query Bus](#query-bus)\n  - [Event Bus](#event-bus)\n- [Subscribe on parents](#subscribe-on-parents)\n- [Services as handlers](#services-as-handlers)\n  - [Auto-register services](#auto-register-services)\n  - [Using your own handling methods](#using-your-own-handling-methods)\n- [Define custom bus](#define-custom-bus)\n- [Testing](#testing)\n\n-----\n\n## Get started:\n\n### Requirenments:\n- PHP 8.2+\n- [PSR-11](https://github.com/php-fig/container) - compatible container (*optional*)\n\n### Install:\n```sh\ncomposer require awd-studio/service-buses\n```\n\n\n## Handling messages:\n\nA message, is nothing, but a simple PHP-object.\n\nIt can contain any data you need, but usually, it's better to provide some immutable messages, that can be serialized.\n```php\n\u003c?php\n\nclass MyMessage {}\n```\n\nAnyway, you are able to extend or implement anything you need.\n```php\n\u003c?php\n\ninterface MessageInterface {}\n\nabstract class ParentMessage {}\n\nfinal class MyMessage extends ParentMessage implements MessageInterface {}\n```\n\nA handler-locator is a repository for handlers.\n\nWith them, we can assign a handler to particular messages.\nLibrary provides some handler locators, for example - a locator to store handlers in memory:\n```php\n\u003c?php\n\nuse AwdStudio\\Bus\\Handler\\InMemoryHandlerLocator;\n\n$handlers = new InMemoryHandlerLocator();\n\n// To assign a handler we can call a method `add`.\n// As a \"messageId\" we send the FCQN of a message that we assign on.\n// A handler must be any callable PHP-item. \n$handlers-\u003eadd(\\stdClass::class, static function (\\stdClass $message): void {});\n\n// Now, we've got a handler that handles a message of type \"stdClass\".\n// But, we can add more than one handler per message. \n// Actually, it's not limited, but keep in mind the patterns\n// such Command-bus or Query-bus that suppose to use the only one handler\n// per a message that they handle.\n// So, we can add more handlers to same message, for example a callable object:\n$handler = new class {\n    public function __invoke(\\stdClass $message): void {}\n};\n$handlers-\u003eadd(\\stdClass::class, $handler);\n\n// So now, we have 2 handlers that are going to be released \n// when somebody tries get them:\n$handlers-\u003eget(\\stdClass::class);\n\n// To check if there are some handlers for certain message \n// there is a method `has`:\n$handlers-\u003ehas(\\stdClass::class); // true|false\n```\n\n\nTo handle a message, the bus needs to be called. \nFor instance, we have a bus that extends provided SimpleBus. \n\nWe're gonna use a \n```php\n\u003c?php\n\nuse AwdStudio\\Bus\\Handler\\InMemoryHandlerLocator;\n\n// We need to use a handler locator, from which a bus will get handlers\n$bus = new class(new InMemoryHandlerLocator()) extends \\AwdStudio\\Bus\\SimpleBus {\n    // We need to provide a method that will handle our message\n    public function handle(object $message): void \n    {\n        // Our parent allows us to iterate all handlers \n        // that assigned to certain message\n        foreach ($this-\u003ehandleAll($message) as $result) {\n            echo $result;\n        }\n    }\n};\n\n// To use a bus, we call a provided method:\n$bus-\u003ehandle(new \\stdClass());\n```\n\n\n\n## Predefined buses:\n\nThere are a few predefined buses: \n- `\\AwdStudio\\Command\\CommandBus` *(The Command-bus pattern akka `C` in `CQRS`)*\n  - `\\AwdStudio\\Command\\SimpleCommandBus` - Handles a command, via single handler.\n  \n\n- `\\AwdStudio\\Query\\QueryBus` *(The Query-bus pattern akka `Q` in `CQRS`)*\n  - `\\AwdStudio\\Query\\SimpleQueryBus` - Handles a query, via single handler.\n\n\n- `\\AwdStudio\\Event\\EventBus` *(Observer-subscriber pattern)*\n  - `\\AwdStudio\\Event\\SimpleEventBus` - Dispatches an event, to each subscriber (can be `\u003e= 0`).\n\n### Command-bus:\n\n```php\n\u003c?php\n\nuse AwdStudio\\Bus\\Handler\\InMemoryHandlerLocator;\nuse AwdStudio\\Command\\SimpleCommandBus;\n\nclass MyCommand {\n    // Messages might be any of PHP class.\n    // No any of implementation or extending required.\n}\n\n$handlers = new InMemoryHandlerLocator();\n// Register a handler. It can be any callable thing.\n$handlers-\u003eadd(MyCommand::class, static function (MyCommand $command): void {});\n\n$bus = new SimpleCommandBus($handlers);\n\n$bus-\u003ehandle(new MyCommand());\n```\n\n\n### Query-bus:\n\n```php\n\u003c?php\n\nuse AwdStudio\\Bus\\Handler\\InMemoryHandlerLocator;\nuse AwdStudio\\Query\\SimpleQueryBus;\n\nclass MyQuery {\n    // Messages might be any of PHP class.\n    // No any of implementation or extending required.\n}\n\n$handlers = new InMemoryHandlerLocator();\n// Register a handler. It can be any callable thing.\n$handlers-\u003eadd(MyQuery::class, static function (MyQuery $query): string {\n    return 'foo';\n});\n\n$bus = new SimpleQueryBus($handlers);\n\n$result = $bus-\u003ehandle(new MyQuery());\n\n// Result will be:\n// -\u003e prefix foo suffix\n```\n\n\n### Event-bus:\n\n```php\n\u003c?php\n\nuse AwdStudio\\Bus\\Handler\\InMemoryHandlerLocator;\nuse AwdStudio\\Event\\SimpleEventBus;\n\nclass MyEvent {\n    // Messages might be any of PHP class.\n    // No any of implementation or extending required.\n}\n\n$subscribers = new InMemoryHandlerLocator();\n// Register a handler. It can be any callable thing.\n$subscribers-\u003eadd(MyEvent::class, static function (MyEvent $event): void {});\n// As the event-bus pattern allows to provide any amount of subscribers\n// we cah add more of them:\n$subscribers-\u003eadd(MyEvent::class, static function (MyEvent $event): void {});\n\n$bus = new SimpleEventBus($subscribers);\n\n$bus-\u003ehandle(new MyEvent());\n\n// After that, the event is delivered to each subscriber.\n```\n\n\n## Subscribe on parents\n\nThe library allows subscribing not only on a certain class, but on all of its parents - either a parent or an implementation from any level.\n\n```php\n\u003c?php\n\nuse AwdStudio\\Bus\\Handler\\ParentsAwareClassHandlerRegistry;\nuse AwdStudio\\Bus\\Handler\\PsrContainerClassHandlerRegistry;\nuse Psr\\Container\\ContainerInterface;\n\nclass MyPsr11Container implements ContainerInterface {}\n\ninterface Foo {}\nabstract class Bar {}\nfinal class Baz extends Bar implements Foo {}\n\nclass Handler\n{\n    // You can subscribe on any of level\n    public function __invoke(Foo $message): void {}\n    // ..or\n    public function __invoke(Bar $message): void {}\n    // ..or\n    public function __invoke(Baz $message): void {}\n}\n\n$handlerRegistry = new ParentsAwareClassHandlerRegistry(new PsrContainerClassHandlerRegistry(new MyPsr11Container()));\n```\n\n\n## Services as handlers\n\nOf course, to resolve the only callbacks as handlers is not such a convenient way to build projects. \nFortunately, we have standards as a `PSR-11` for such common use-cases as implementation of `DIP`. \nAnd, the library provides ability to use those containers as service locators for resolving handlers as DI.\n\nTo use it, there is a decorator for a handler-locator, that can be used for registering handlers with just FCQN. \nAs a dependency it accepts any of `Psr\\Container\\ContainerInterface`, that supposed to resolve handlers.\n\n```php\n\u003c?php\n\nuse AwdStudio\\Bus\\Handler\\PsrContainerClassHandlerRegistry;\nuse AwdStudio\\Bus\\SimpleBus;\nuse Psr\\Container\\ContainerInterface;\n\nclass MyPsr11Container implements ContainerInterface\n{\n    private $dependencies;\n\n    public function __construct(array $dependencies)\n    {\n        $this-\u003edependencies = $dependencies;\n    }\n\n    public function has($id): bool\n    {\n        return \\in_array($id, $this-\u003edependencies, true);\n    }\n\n    public function get($id): object\n    {\n        return $id();\n    }\n}\n\nclass StdClassHandler\n{\n    public function __invoke(\\stdClass $message): void\n    {\n        $message-\u003efoo = 'foo';\n    }\n}\n\n$serviceLocator = new MyPsr11Container([StdClassHandler::class]);\n$handlerRegistry = new PsrContainerClassHandlerRegistry($serviceLocator);\n\n// To assign a handler use a defined method:\n$handlerRegistry-\u003eregister(\\stdClass::class, StdClassHandler::class);\n\n// And pass them as a handler-locator to a bus\n$bus = new class ($handlerRegistry) extends SimpleBus {\n    public  function handle(object $message): void \n    {\n        foreach ($this-\u003ehandleAll($message) as $result) {\n            echo $result;\n        }\n    }\n};\n\n// After that, you can call handling as usual:\n$bus-\u003ehandle(new \\stdClass()); // The handler will be executed\n```\n\n\n### Auto-register services\n\nThere is even a decorator to subscribe callbacks automatically, by their signature, that supposed to contain a type-hint as the very first parameter.\n\n```php\n\u003c?php\n\nuse AwdStudio\\Bus\\Handler\\AutoRegisterHandlersRegistryClass;\nuse AwdStudio\\Bus\\Handler\\PsrContainerClassHandlerRegistry;\n\n$psrRegistry = new PsrContainerClassHandlerRegistry(new  MyPsr11Container());\n$autoRegistry = new AutoRegisterHandlersRegistryClass($psrRegistry);\n\n// Now, you can add a callback to assign a handler automatically.\n// Just be sure, that it has a correct type-hint of a message that it handles.\n$handler = static function (\\stdClass $message): void { };\n$autoRegistry-\u003eautoAdd($handler); // It will be called within the stdClass' messages.\n\n// And this is not all it can! \n// If you use services as handlers - you also can register them automatically. \n// Suppose we have this handler, that can be resolved from our container:\nclass Handler {\n    public function __invoke(\\stdClass $message): void { }\n}\n\n// We can register it like so:\n$autoRegistry-\u003eautoRegister(Handler::class);\n\n// That's all..\n```\n\n\n### Using your own handling methods\n\nIf you don't like invokable services, or somehow need to use handlers that handle via different methods - this is not a problem at all. \n\nJust pass the name of a method while registering:\n\n```php\n\u003c?php\n\nuse AwdStudio\\Bus\\Handler\\PsrContainerClassHandlerRegistry;\n\nclass Handler {\n    public function handle(\\stdClass $message): void { }\n}\n\n// Any registry can manage with it out of the box\n$psrRegistry = new PsrContainerClassHandlerRegistry(new  MyPsr11Container());\n$psrRegistry-\u003eregister(\\stdClass::class, Handler::class, 'handle');\n// The 3rd argument tells which method is in charge of handling.\n```\n\n\n\n## Define custom bus\n\nTo define your own bus, you can extend one of predefined ones. \nYou have 2 options:\n```php\n\u003c?php\n\nuse AwdStudio\\Bus\\SimpleBus;\n\nclass MyBus extends SimpleBus\n{\n    public function handle(object $message): string \n    {\n        $result = '';\n        foreach ($this-\u003ehandleAll($message) as $handled) {\n            $result .= $handled;\n        }\n    \n        return $result;\n    }\n}\n```\nThe SimpleBus provides you an ability to handle messages with only handles.\n\n```php\n\u003c?php\n\nuse AwdStudio\\Bus\\SimpleBus;\n\nclass MyBus extends SimpleBus\n{\n    public function handle(object $message): string \n    {\n        $result = '';\n        foreach ($this-\u003ehandleMessage($message) as $chain) {\n            $result .= $chain();\n        }\n    \n        return $result;\n    }\n}\n```\n\n\n-----\n\n\n## Testing:\n```bash\ncomposer setup-dev\ncomposer test\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawd-studio%2Fservice-buses","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fawd-studio%2Fservice-buses","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawd-studio%2Fservice-buses/lists"}