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:
So we decided to support the Event-driven Architecture by enabling the Event-driven Programming with Wechaty by publishing the wechaty-cqrs NPM module.
bus$
with the from()
function.commands
, queries
, responses
, and events
payload creators.execute$()
helper function for sending the events
to the bus and get back the response.events$
for the Wechaty eventssayables
for build all the message contentsnpm install wechaty-cqrs wechaty
Here'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 needed
Learn more from the source code
Read CQRS Wechaty API Reference at: https://paka.dev/npm/wechaty-cqrs
The following steps need to be followed:
camelCase
to SNAKE_CASE
.
(i.e. dingCommand
-> DING_COMMAMD
)execute$()
helper function for sending the events to the bus
and get back the response, with automatically type inferring.Learn more from PR #3
execute$
with duck.actions
builders.Huan LI (李卓桓), Microsoft Regional Director, zixia@zixia.net