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.
Image source: Introducing Derivative Event Sourcing
Command query responsibility separation (CQRS) generalises CQS to message-driven and event-driven architectures: it applies the CQS principle by using separate Query and Command messages to retrieve and modify data, respectively.
Image source: CQRS (command query responsibility segregation)
Can we use Wechaty by only sending / receiving the Plain Old JavaScript Object (POJO)?
That's an Event-driven way, which will give us the following benifites:
- Better integration with Domain-driven Design (DDD)
 - Decouple the sub-systems with the Wechaty instance completely
 - Enable using Wechaty with Microservices
 - Make it possible for providing an API endpoint with JSON request/responses
 - etc.
 
So we decided to support the Event-driven Architecture by enabling the Event-driven Programming with Wechaty by publishing the wechaty-cqrs NPM module.
- Convert Wechaty instance to a messaging 
bus$with thefrom()function. - Well-defined 
commands,queries,responses, andeventspayload creators. - A great 
execute$()helper function for sending the events to the bus and get back the response. - Well-defined 
events$for the Wechaty events - Well-defined 
sayablesfor build all the message contents - Static typing with TypeScript with all events & streams
 - Working perfect with the powerful RxJS
 
npm install wechaty-cqrs wechatyHere's the CQRS version of the Wechaty bot usage:
import * as CQRS    from 'wechaty-cqrs'
import * as WECHATY from 'wechaty'
import { filter, map, mergeMap }  from 'rxjs/operators'
const wechaty = WECHATY.WechatyBuilder.build()
await wechaty.init()
const bus$ = CQRS.from(wechaty)
bus$.pipe(
  filter(CQRS.is(CQRS.events.MessageReceivedEvent)),
  // MessageReceivedEvent -> Sayable
  map(messageId => CQRS.queries.GetSayablePayloadQuery(
    messageReceivedEvent.meta.puppetId,
    messageId,Diagrams
  )),
  mergeMap(CQRS.execute$(bus$)),
  // Log `sayable` to console
).subscribe(sayable =>
  console.info('Sayable:', sayable),
)
bus$.next(CQRS.commands.StartCommand(wechaty.puppet.id))Learn how to build a Ding Dong BOT with CQRS from our examples/ding-dong-bot.ts
Here's a video introduction for CQRS Wechaty with live demo, presented by Huan:
YouTube: https://youtu.be/kauxyPVa0jo
The getting started ding-dong-bot.ts in the video: https://github.com/wechaty/getting-started/blob/main/examples/cqrs/ding-dong-bot.ts
graph LR
  classDef event fill:DarkGoldenRod
  classDef command fill:blue
  classDef query fill:green
Diagrams
  subgraph Command
    C(VerbNounCommand):::command
  end
  subgraph Response
    RC(VerbNounCommandResponse)
    RQ(GetNounQueryResponse)
  end
    
  subgraph Query
    Q(GetNounQuery):::query
  end
  subgraph Event
    ER(ReceivedEvent):::event
  end
  C-->RC
  ER-->ER
  Q-->RQ
    sequenceDiagram
    participant Bus
    participant Redux
    participant Wechaty
    Bus->>Redux: ExecuteCommand
    Redux->>Wechaty: Call
    Wechaty->>Redux: Call Return (void)
    Redux->>Bus: ExecuteCommandResponse
    sequenceDiagram
    participant Bus
    participant Redux
    participant Wechaty
    Bus->>Redux: GetNounQuery
    Redux->>Wechaty: Call
    Wechaty->>Redux: Call Return (value)
    Redux->>Bus: GetNounQueryResponse
    sequenceDiagram
    participant Bus
    participant Redux
    participant Wechaty
    Wechaty->>Redux: ReceivedEvent
    Redux->>Bus: ReceivedEvent
    A Data Transfer Object (DTO) is an object that carries data between processes.
CQRS Wechaty has encapsulated all the events to DTOs, exported by:
// You will get DTOs from CQRS.{commands,queries} module
import * as CQRS from 'wechaty-cqrs'
/**
 * Examples: building Data Transfer Object for
 *  - `DingCommand`
 *  - `GetIsLoggedInQuery
 */
const dingCommand         = CQRS.commands.DingCommand(...)
const getIsLoggedInQuery  = CQRS.queries.GetIsLoggedInQuery(...)
// Use them as you neededLearn more from the source code
Read CQRS Wechaty API Reference at: https://paka.dev/npm/wechaty-cqrs
The following steps need to be followed:
- Duck
 - DTO
 - Writing unit tests.
 
- Layers in DDD microservices
 - Effective Aggregate Design
 - Domain-Application-Infrastructure Services pattern
 
- rename all duck events' name from 
camelCasetoSNAKE_CASE. (i.e.dingCommand->DING_COMMAMD) 
- Classify action builders so that they will be compatible with Class events with NestJS #1
 execute$()helper function for sending the events to the bus and get back the response, with automatically type inferring.- lots of typing enhancements.
 
Learn more from PR #3
- v0.9 (Mar 17,2022): Refactoring(clean) module exports
 - v0.7 (Mar 16, 2022): Refactoring 
execute$withduck.actionsbuilders. - v0.6 (Mar 13, 2022): Alpha release.
 - v0.4 (Mar 13, 2022): CQRS Ding/Dong BOT works as expected.
 - v0.2 (Mar 11, 2022): Unit tests all passed, DevOps enabled.
 - v0.0.1 (Mar 6, 2022): Init README & Draft design.
 
Huan LI (李卓桓), Microsoft Regional Director, [email protected]
- Code & Docs © 2022 Huan (李卓桓) <[email protected]>
 - Code released under the Apache-2.0 License
 - Docs released under Creative Commons
 



