{"id":21894775,"url":"https://github.com/zumba/cqrs","last_synced_at":"2025-08-19T01:08:14.726Z","repository":{"id":47368296,"uuid":"379395099","full_name":"zumba/cqrs","owner":"zumba","description":"CQRS Framework","archived":false,"fork":false,"pushed_at":"2023-11-14T14:49:44.000Z","size":169,"stargazers_count":2,"open_issues_count":2,"forks_count":0,"subscribers_count":29,"default_branch":"main","last_synced_at":"2025-07-23T10:08:16.151Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/zumba.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-06-22T20:39:07.000Z","updated_at":"2023-11-14T14:00:36.000Z","dependencies_parsed_at":"2024-11-28T16:16:42.217Z","dependency_job_id":null,"html_url":"https://github.com/zumba/cqrs","commit_stats":{"total_commits":116,"total_committers":9,"mean_commits":12.88888888888889,"dds":0.5344827586206897,"last_synced_commit":"bd0d308d9698e32da6d9e88adbb00951a3faf4df"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/zumba/cqrs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zumba%2Fcqrs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zumba%2Fcqrs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zumba%2Fcqrs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zumba%2Fcqrs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zumba","download_url":"https://codeload.github.com/zumba/cqrs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zumba%2Fcqrs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271083721,"owners_count":24696355,"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","status":"online","status_checked_at":"2025-08-18T02:00:08.743Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2024-11-28T13:28:25.550Z","updated_at":"2025-08-19T01:08:14.687Z","avatar_url":"https://github.com/zumba.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Zumba ***CQRS***\n\n[Command Query Responsibility Segregation (*CQRS*)][1]\nis an established design pattern.  This library aims to implement the *plumbing* needed to create CQRS commands and\nqueries.\n\n---\n\n- [Command Bus Usage][3]\n  - [Creating a command][4]\n  - [Creating a command handler][5]\n  - [Dispatching a command][6]\n- [Query Bus Usage][7]\n  - [Creating a query handler][8]\n  - [Dispatching a query][9]\n- [Providers][10]\n  - [Class provider][11]\n  - [Method provider][12]\n  - [Simple dependency provider][13]\n  - [Create a custom provider][14]\n- [Middleware][15]\n  - [Logger middleware][16]\n  - [Custom middleware][17]\n\n---\n## Command Bus Usage\n\nThe command bus allows commands with handlers to be dispatched with the result being either a `Success` or `Failure`.\n\nBy design, the command must not return any data.\n\n### Creating a command\n\nCreating a command DTO to dispatch to the command bus requires us to extend the abstract `Command` class.\n\n```php\n\u003c?php\n\nnamespace My\\Command;\n\nuse Zumba\\CQRS\\Command\\Command;\nuse Zumba\\CQRS\\Command\\WithProperties;\n\nfinal class MyCommand extends Command implements WithProperties\n{\n    protected string $message;\n\n    public static function fromArray(array $props): Command\n    {\n        $command = new static();\n        $command-\u003emessage = $props['message'];\n        return $command;\n    }\n}\n```\n\n### Creating a command handler\n\nWith the `MyCommand` created from the [Creating a command][4] section, we need to handle this command to perform an action.\n\n\u003e Tip: The command handler is a great place to introduce any dependencies to be injected.\n\n```php\n\u003c?php\n\nnamespace My\\Command;\n\nuse Zumba\\CQRS\\Command\\Command;\nuse Zumba\\CQRS\\Command\\CommandResponse;\nuse Zumba\\CQRS\\Command\\Handler;\nuse Zumba\\CQRS\\CommandService;\n\nfinal class MyCommandHandler implements Handler\n{\n    public function handle(Command $command, CommandService $commandService): CommandResponse\n    {\n        echo $command-\u003emessage;\n        return CommandResponse::fromSuccess();\n    }\n}\n```\n\n### Dispatching a command\n\nWith both a command and handler for that command created, we're ready to dispatch our command to the command bus.\n\nA default batteries-included command bus can be instanced that we can dispatch our command DTOs to the appropriate handlers.\n\n```php\n\u003c?php\n\nuse My\\Command\\MyCommand;\nuse Zumba\\CQRS\\CommandBus;\nuse Zumba\\CQRS\\Response\\Success;\n\n$commandBus = CommandBus::defaultBus();\n$command = MyCommand::fromProperties([\n    'message' =\u003e 'Hello!',\n]);\n\n$result = $commandBus-\u003edispatch($command);\n// Hello! is echoed\nvar_dump($result instanceof Success);\n// true\n```\n\n---\n\n## Query Bus Usage\n\nSimilar to the command bus, the query bus operates with a query DTO with an accompanying handler dispatched to the query bus in order to get resulting data.\n\n### Creating a query\n\nCreating a query DTO to dispatch to the query bus requires us to extend the abstract `Query` class.\n\n```php\n\u003c?php\n\nnamespace My\\Query;\n\nuse Zumba\\CQRS\\Query\\Query;\nuse Zumba\\CQRS\\Query\\WithProperties;\n\nfinal class MyQuery extends Query implements WithProperties\n{\n    protected string $ID;\n\n    public static function fromArray(array $props): Query\n    {\n        $query = new static();\n        $query-\u003eID = $props['id'];\n        return $query;\n    }\n}\n```\n\n### Create a query handler\n\nQuery handlers are designed to return a query response depending on the kind of data to return.\n\nIn the above created `MyQuery` DTO, let's assume that this is retrieving a key-value result for a specific entity.\nWe would build our response using the `Map` response type:\n\n```php\n\u003c?php\n\nnamespace My\\Query;\n\nuse Zumba\\CQRS\\Query\\Handler;\nuse Zumba\\CQRS\\Query\\Query;\nuse Zumba\\CQRS\\Query\\QueryResponse;\n\nfinal class MyQueryHandler implements Handler\n{\n    public function handle(Query $query): QueryResponse\n    {\n        // Do appropriate lookups\n        return QueryResponse::fromMap([\n            'id' =\u003e 'example-id',\n            'name' =\u003e 'example-name',\n        ]);\n    }\n}\n```\n\n### Dispatching a query\n\nAs with [dispatching a command][6], we can use the batteries-included query bus which can be included using the `QueryBusTrait`.\n\n```php\nnamespace My\\Query;\n\nuse Zumba\\CQRS\\QueryBusTrait;\n\nclass Example {\n    use QueryBusTrait;\n\n    public function query(): array\n    {\n        $response = $this-\u003equeryBus()-\u003edispatch(new MyQuery([\n            'id' =\u003e 'example-id',\n        ]));\n        return [\n            'id' =\u003e $response['id'],\n            'name' =\u003e $response['name'],\n        ];\n    }\n\n}\n\nvar_dump((new Example)-\u003equery());\n// [\n//     'id' =\u003e 'example-id',\n//     'name' =\u003e 'example-name',\n// ]\n```\n\n## Providers\n\nProviders offer the ability for the command bus to be able to supply a handler for a DTO dispatched to the bus.\n\n`zumba\\cqrs` provides several providers out of the box.\n\n### Class Provider\n\nThe `Zumba\\CQRS\\Provider\\ClassProvider` can be used to construct handlers for a DTO via a handler factory.\nThis can be used when the handlers have complex dependencies (ie dependencies that require configuration).\n\nTo utilize this, create a handler factory in the same namespace as the DTO. Assume our DTO from the [create a command][4] section\nof `MyCommand`, the handler factory would look like this:\n\n```php\n\u003c?php\n\nnamespace My\\Command;\n\nuse Zumba\\CQRS\\Command\\HandlerFactory;\nuse Zumba\\CQRS\\Command\\Handler;\n\nclass MyCommandHandlerFactory implements HandlerFactory\n{\n    public static function make(): Handler\n    {\n        return new MyCommandHandler();\n    }\n}\n```\n\n### Method Provider\n\nThe `Zumba\\CQRS\\Provider\\MethodProvider` is similar to the `ClassProvider` except the handler itself can serve as its own factory.\n\nThe `MyCommandHandler` can be adapted from the [create a command handler][5] section:\n\n```php\n\u003c?php\n\nnamespace My\\Command;\n\nuse Zumba\\CQRS\\Command\\Command;\nuse Zumba\\CQRS\\Command\\CommandResponse;\nuse Zumba\\CQRS\\Command\\Handler;\nuse Zumba\\CQRS\\Command\\HandlerFactory;\nuse Zumba\\CQRS\\CommandService;\n\nfinal class MyCommandHandler implements Handler, HandlerFactory\n{\n    public function handle(Command $command, CommandService $commandService): CommandResponse\n    {\n        echo $command-\u003emessage;\n        return CommandResponse::fromSuccess();\n    }\n\n    public static function make(): Handler\n    {\n        return new static();\n    }\n}\n```\n\n### Simple Dependency Provider\n\nIn cases where all dependencies can be instantiated without parameters, the `Zumba\\CQRS\\Provider\\SimpleDependencyProvider` can be used to construct the handler without the need of a `HandlerFactory`.\n\n### Create a custom provider\n\nTo create your own custom provider, implement the `Zumba\\CQRS\\Provider` interface and create the bus with the custom provider.\nYou can also provide additional providers to fallback on if your CustomProvider can't accommodate the DTO.\n\n```php\n\u003c?php\n\nuse My\\Provider\\CustomProvider;\n\n$bus = CommandBus::fromProviders(\n    new CustomProvider(),\n    new ClassProvider(),\n);\n```\n\n---\n\n## Middleware\n\nThe CQRS library is equipped to allow for middleware to be attached to a command or query bus to allow for generic actions to occur for any or all DTOs.\n\n### Logger middleware\n\nThe logger middleware allows logging all DTOs that are dispatched to through a bus.\n\n```php\n\u003c?php\n\nuse Psr\\Log\\LogLevel;\nuse Psr\\Log\\NullLogger;\nuse Zumba\\CQRS\\CommandBus;\nuse Zumba\\CQRS\\MiddlewarePipeline;\nuse Zumba\\CQRS\\Middleware\\Logger;\n\n$bus = CommandBus::defaultBus();\n// Substitute with a real Psr logger.\n$logger = new NullLogger();\n\n$middlewarePipeline = MiddlewarePipeline::fromMiddleware(\n    Logger::fromLoggerAndLevel(\n        $logger,\n        LogLevel::INFO,\n    )\n);\n\n$bus = $bus-\u003ewithMiddleware($middlewarePipeline);\n```\n\n### Custom middleware\n\nCustom middleware can be included in the `MiddlewarePipeline` as long as it implements the `Middleware` interface.\n\nThe `handle` method will accept a DTO and a `callable` to which to continue the process.\n\nSee the [`Logger`][2] middleware as an example.\n\n\n[1]: https://docs.microsoft.com/en-us/azure/architecture/patterns/cqrs\n[2]: https://github.com/zumba/cqrs/blob/2b1c5227452337f550266dc1bb1217ff7a40ef28/src/Middleware/Logger.php\n[3]: #command-bus-usage\n[4]: #creating-a-command\n[5]: #creating-a-command-handler\n[6]: #dispatching-a-command\n[7]: #query-bus-usage\n[8]: #create-a-query-handler\n[9]: #dispatching-a-query\n[10]: #providers\n[11]: #class-provider\n[12]: #method-provider\n[13]: #simple-dependency-provider\n[14]: #create-a-custom-provider\n[15]: #middleware\n[16]: #logger-middleware\n[17]: #custom-middleware\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzumba%2Fcqrs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzumba%2Fcqrs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzumba%2Fcqrs/lists"}