{"id":28445061,"url":"https://github.com/wechaty/cqrs","last_synced_at":"2025-06-29T20:33:05.385Z","repository":{"id":42715088,"uuid":"466639186","full_name":"wechaty/cqrs","owner":"wechaty","description":"An event-driven architecture wrapper for Wechaty that applies the CQS principle by using separate Query and Command messages to retrieve and modify the bot state, respectively.","archived":false,"fork":false,"pushed_at":"2023-07-26T12:45:18.000Z","size":609,"stargazers_count":6,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-06T10:11:35.039Z","etag":null,"topics":["cqrs","ddd","domain-driven-design","ducks","event-driven","redux","rxjs"],"latest_commit_sha":null,"homepage":"https://paka.dev/npm/wechaty-cqrs","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wechaty.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}},"created_at":"2022-03-06T04:51:22.000Z","updated_at":"2025-04-21T10:58:40.000Z","dependencies_parsed_at":"2024-01-23T21:15:49.815Z","dependency_job_id":"8f2b871e-4ab9-4e33-a489-28e52e6f8428","html_url":"https://github.com/wechaty/cqrs","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/wechaty/cqrs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wechaty%2Fcqrs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wechaty%2Fcqrs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wechaty%2Fcqrs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wechaty%2Fcqrs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wechaty","download_url":"https://codeload.github.com/wechaty/cqrs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wechaty%2Fcqrs/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262663388,"owners_count":23345046,"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":["cqrs","ddd","domain-driven-design","ducks","event-driven","redux","rxjs"],"created_at":"2025-06-06T10:11:12.090Z","updated_at":"2025-06-29T20:33:05.317Z","avatar_url":"https://github.com/wechaty.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CQRS Wechaty\n\n[![NPM Version](https://img.shields.io/npm/v/wechaty-cqrs?color=brightgreen)](https://www.npmjs.com/package/wechaty-cqrs)\n[![NPM](https://github.com/wechaty/cqrs/workflows/NPM/badge.svg)](https://github.com/wechaty/cqrs/actions?query=workflow%3ANPM)\n[![Ducksify Extension](https://img.shields.io/badge/Redux-Ducksify-yellowgreen)](https://github.com/huan/ducks#3-ducksify-extension-currying--ducksify-interface)\n[![ES Modules](https://img.shields.io/badge/ES-Modules-brightgreen)](https://github.com/Chatie/tsconfig/issues/16)\n\nAn event-driven architecture wrapper for Wechaty\nthat applies the CQS principle\nby using separate Query and Command messages\nto retrieve and modify the bot state,\nrespectively.\n\n![Command Query Responsibility Segregation (CQRS) Wechaty](docs/images/cqrs-wechaty.png)\n\n\u003e Image source: [Introducing Derivative Event Sourcing](https://www.confluent.io/blog/event-sourcing-vs-derivative-event-sourcing-explained/)\n\n## Command Query Responsibility Separation (CQRS)\n\n\u003e Command query responsibility separation (CQRS) generalises CQS\n  to message-driven and event-driven architectures:\n  it applies the CQS principle by using separate Query and Command messages\n  to retrieve and modify data, respectively.\n\u003e\n\u003e \u0026mdash; [Wikipedia: Command–query separation](https://en.wikipedia.org/wiki/Command%E2%80%93query_separation)\n\n![Command Query Responsibility Segregation (CQRS) Pattern](docs/images/cqrs-pattern.png)\n\n\u003e Image source: [CQRS (command query responsibility segregation)](https://www.techtarget.com/searchapparchitecture/definition/CQRS-command-query-responsibility-segregation)\n\n## Motivation\n\nCan we use Wechaty by only sending / receiving the [Plain Old JavaScript Object (POJO)](https://masteringjs.io/tutorials/fundamentals/pojo)?\n\nThat's an Event-driven way, which will give us the following benifites:\n\n1. Better integration with Domain-driven Design (DDD)\n1. Decouple the sub-systems with the Wechaty instance completely\n1. Enable using Wechaty with Microservices\n1. Make it possible for providing an API endpoint with JSON request/responses\n1. etc.\n\nSo we decided to support the  Event-driven Architecture\nby enabling the Event-driven Programming with Wechaty\nby publishing the [wechaty-cqrs](https://npmjs.com/package/wechaty-cqrs) NPM module.\n\n## Features\n\n1. Convert Wechaty instance to a messaging `bus$` with the `from()` function.\n1. Well-defined `commands`, `queries`, `responses`, and `events` payload creators.\n1. A great `execute$()` helper function for sending the events\n  to the bus and get back the response.\n1. Well-defined `events$` for the Wechaty events\n1. Well-defined `sayables` for build all the message contents\n1. Static typing with TypeScript with all events \u0026 streams\n1. Working perfect with the powerful RxJS\n\n## Usage\n\n### Install\n\n```sh\nnpm install wechaty-cqrs wechaty\n```\n\n### Quick start\n\nHere's the CQRS version of the Wechaty bot usage:\n\n```ts\nimport * as CQRS    from 'wechaty-cqrs'\nimport * as WECHATY from 'wechaty'\nimport { filter, map, mergeMap }  from 'rxjs/operators'\n\nconst wechaty = WECHATY.WechatyBuilder.build()\nawait wechaty.init()\n\nconst bus$ = CQRS.from(wechaty)\n\nbus$.pipe(\n  filter(CQRS.is(CQRS.events.MessageReceivedEvent)),\n  // MessageReceivedEvent -\u003e Sayable\n  map(messageId =\u003e CQRS.queries.GetSayablePayloadQuery(\n    messageReceivedEvent.meta.puppetId,\n    messageId,Diagrams\n  )),\n  mergeMap(CQRS.execute$(bus$)),\n  // Log `sayable` to console\n).subscribe(sayable =\u003e\n  console.info('Sayable:', sayable),\n)\n\nbus$.next(CQRS.commands.StartCommand(wechaty.puppet.id))\n```\n\nLearn how to build a Ding Dong BOT with CQRS from our [examples/ding-dong-bot.ts](https://github.com/wechaty/cqrs/blob/main/examples/ding-dong-bot.ts)\n\n## Getting Started\n\nHere's a video introduction for CQRS Wechaty with live demo, presented by Huan:\n\n[![CQRS Wechaty Getting Started](docs/images/cqrs-wechaty-getting-started.webp)](https://youtu.be/kauxyPVa0jo)\n\n\u003e YouTube: \u003chttps://youtu.be/kauxyPVa0jo\u003e\n\nThe getting started [ding-dong-bot.ts](https://github.com/wechaty/getting-started/blob/main/examples/cqrs/ding-dong-bot.ts)\nin the video: \u003chttps://github.com/wechaty/getting-started/blob/main/examples/cqrs/ding-dong-bot.ts\u003e\n\n## Architecture Diagrams\n\n![CQRS Events Structure](docs/images/cqrs-events-diagram.svg)\n\n```mermaid\ngraph LR\n  classDef event fill:DarkGoldenRod\n  classDef command fill:blue\n  classDef query fill:green\nDiagrams\n  subgraph Command\n    C(VerbNounCommand):::command\n  end\n\n  subgraph Response\n    RC(VerbNounCommandResponse)\n    RQ(GetNounQueryResponse)\n  end\n    \n  subgraph Query\n    Q(GetNounQuery):::query\n  end\n\n  subgraph Event\n    ER(ReceivedEvent):::event\n  end\n\n  C--\u003eRC\n\n  ER--\u003eER\n\n  Q--\u003eRQ\n```\n\n### Command\n\n```mermaid\nsequenceDiagram\n    participant Bus\n    participant Redux\n    participant Wechaty\n\n    Bus-\u003e\u003eRedux: ExecuteCommand\n    Redux-\u003e\u003eWechaty: Call\n    Wechaty-\u003e\u003eRedux: Call Return (void)\n    Redux-\u003e\u003eBus: ExecuteCommandResponse\n```\n\n### Query\n\n```mermaid\nsequenceDiagram\n    participant Bus\n    participant Redux\n    participant Wechaty\n\n    Bus-\u003e\u003eRedux: GetNounQuery\n    Redux-\u003e\u003eWechaty: Call\n    Wechaty-\u003e\u003eRedux: Call Return (value)\n    Redux-\u003e\u003eBus: GetNounQueryResponse\n```\n\n### Event\n\n```mermaid\nsequenceDiagram\n    participant Bus\n    participant Redux\n    participant Wechaty\n\n    Wechaty-\u003e\u003eRedux: ReceivedEvent\n    Redux-\u003e\u003eBus: ReceivedEvent\n```\n\n## Data Transfer Object (DTO)\n\nA [Data Transfer Object (DTO)](https://en.wikipedia.org/wiki/Data_transfer_object)\nis an object that carries data between processes.\n\nCQRS Wechaty has encapsulated all the events to DTOs, exported by:\n\n```ts\n// You will get DTOs from CQRS.{commands,queries} module\nimport * as CQRS from 'wechaty-cqrs'\n\n/**\n * Examples: building Data Transfer Object for\n *  - `DingCommand`\n *  - `GetIsLoggedInQuery\n */\nconst dingCommand         = CQRS.commands.DingCommand(...)\nconst getIsLoggedInQuery  = CQRS.queries.GetIsLoggedInQuery(...)\n\n// Use them as you needed\n```\n\nLearn more from the [source code](src/dto/actions/)\n\n## API Reference\n\nRead CQRS Wechaty API Reference at: \u003chttps://paka.dev/npm/wechaty-cqrs\u003e\n\n## How to contribute\n\n### How to add a new command/query\n\nThe following steps need to be followed:\n\n1. Duck\n    1. Add a new [type](src/duck/types/queries.ts) to the `commands` or `queries`\n    1. Add a new [action](src/duck/actions/queries.ts) to the `request` and `response`\n    1. Add a new [epic](src/duck/epics/queries) to call the async API\n1. DTO\n    1. Export new added `Command`/`Query` from\n      [commands](src/dto/actions/commands.ts)/[queries](src/dto/actions/queries.ts)\n    1. Export new added `Response` from [responses](src/dto/actions/responses.ts)\n1. Writing unit tests.\n\n## Blogs\n\n- [Refactoring Friday BOT with NestJS, Domain-driven Design (DDD), and CQRS, @huan, Feb 27, 2022](https://wechaty.js.org/2022/02/27/refactoring-friday-bot-with-nestjs-ddd-cqrs/)\n\n## Resources\n\n- [Layers in DDD microservices](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/ddd-oriented-microservice#layers-in-ddd-microservices)\n- Effective Aggregate Design\n  - [Part I: Modeling a Single Aggregate](https://www.dddcommunity.org/wp-content/uploads/files/pdf_articles/Vernon_2011_1.pdf)\n  - [Part II: Making Aggregates Work Together](https://www.dddcommunity.org/wp-content/uploads/files/pdf_articles/Vernon_2011_2.pdf)\n  - [Part III: Gining Insight Through Discovery](https://www.dddcommunity.org/wp-content/uploads/files/pdf_articles/Vernon_2011_3.pdf)\n- [Domain-Application-Infrastructure Services pattern](https://badia-kharroubi.gitbooks.io/microservices-architecture/content/patterns/tactical-patterns/domain-application-infrastructure-services-pattern.html)\n\n## History\n\n### main v1.15 (Apr 20, 2022)\n\n1. rename all duck events' name from `camelCase` to `SNAKE_CASE`.\n  (i.e. `dingCommand` -\u003e `DING_COMMAMD`)\n\n### v1.12 (Mar 27, 2022)\n\n1. Classify action builders so that they will be compatible with Class events\n  with NestJS [#1](https://github.com/wechaty/cqrs/issues/1)\n1. `execute$()` helper function for sending the events to the bus\n  and get back the response, with automatically type inferring.\n1. lots of typing enhancements.\n\nLearn more from [PR #3](https://github.com/wechaty/cqrs/pull/3)\n\n## v0.10 (Mar 17) Beta release\n\n- v0.9 (Mar 17,2022): Refactoring(clean) module exports\n- v0.7 (Mar 16, 2022): Refactoring `execute$` with `duck.actions` builders.\n- v0.6 (Mar 13, 2022): Alpha release.\n- v0.4 (Mar 13, 2022): CQRS Ding/Dong BOT works as expected.\n- v0.2 (Mar 11, 2022): Unit tests all passed, DevOps enabled.\n- v0.0.1 (Mar 6, 2022): Init README \u0026 Draft design.\n\n## Author\n\n[Huan LI](https://github.com/huan)\n([李卓桓](http://linkedin.com/in/zixia)),\n[Microsoft Regional Director](https://rd.microsoft.com/en-us/huan-li),\n\u003czixia@zixia.net\u003e\n\n[![Profile of Huan LI (李卓桓) on StackOverflow](https://stackexchange.com/users/flair/265499.png)](https://stackexchange.com/users/265499)\n\n## Copyright \u0026 License\n\n- Code \u0026 Docs © 2022 Huan (李卓桓) \\\u003czixia@zixia.net\\\u003e\n- Code released under the Apache-2.0 License\n- Docs released under Creative Commons\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwechaty%2Fcqrs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwechaty%2Fcqrs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwechaty%2Fcqrs/lists"}