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.
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.
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.
Features
Convert Wechaty instance to a messaging bus$ with the from() function.
Well-defined commands, queries, responses, and events payload 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 sayables for build all the message contents
Static typing with TypeScript with all events & streams
Working perfect with the powerful RxJS
Usage
Install
npm install wechaty-cqrs wechaty
Quick start
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))
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
CQRS Wechaty
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.
Command Query Responsibility Separation (CQRS)
Motivation
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.
Features
bus$with thefrom()function.commands,queries,responses, andeventspayload creators.execute$()helper function for sending the events to the bus and get back the response.events$for the Wechaty eventssayablesfor build all the message contentsUsage
Install
Quick start
Here’s the CQRS version of the Wechaty bot usage:
Learn how to build a Ding Dong BOT with CQRS from our examples/ding-dong-bot.ts
Getting Started
Here’s a video introduction for CQRS Wechaty with live demo, presented by Huan:
The getting started ding-dong-bot.ts in the video: https://github.com/wechaty/getting-started/blob/main/examples/cqrs/ding-dong-bot.ts
Architecture Diagrams
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-->RQCommand
sequenceDiagram participant Bus participant Redux participant Wechaty Bus->>Redux: ExecuteCommand Redux->>Wechaty: Call Wechaty->>Redux: Call Return (void) Redux->>Bus: ExecuteCommandResponseQuery
sequenceDiagram participant Bus participant Redux participant Wechaty Bus->>Redux: GetNounQuery Redux->>Wechaty: Call Wechaty->>Redux: Call Return (value) Redux->>Bus: GetNounQueryResponseEvent
sequenceDiagram participant Bus participant Redux participant Wechaty Wechaty->>Redux: ReceivedEvent Redux->>Bus: ReceivedEventData Transfer Object (DTO)
A Data Transfer Object (DTO) is an object that carries data between processes.
CQRS Wechaty has encapsulated all the events to DTOs, exported by:
Learn more from the source code
How to contribute
How to add a new command/query
The following steps need to be followed:
commandsorqueriesrequestandresponseCommand/Queryfrom commands/queriesResponsefrom responsesBlogs
Resources
History
main v1.15 (Apr 20, 2022)
camelCasetoSNAKE_CASE. (i.e.dingCommand->DING_COMMAMD)v1.12 (Mar 27, 2022)
execute$()helper function for sending the events to the bus and get back the response, with automatically type inferring.Learn more from PR #3
v0.10 (Mar 17) Beta release
execute$withduck.actionsbuilders.Author
Huan LI (李卓桓), Microsoft Regional Director, zixia@zixia.net
Copyright & License