如何无需控制器调试NestJS CQRS的命令与查询?
针对NestJS CQRS架构的无控制器调试方案
一、通用调试控制器方案
不用给每个命令/查询单独写控制器,直接搞一个通用CQRS调试控制器,接收命令/查询的类型和参数,直接转发到CQRS总线就行。
示例代码:
import { Controller, Post, Body } from '@nestjs/common'; import { CommandBus, QueryBus } from '@nestjs/cqrs'; @Controller('cqrs-debug') export class CqrsDebugController { constructor( private readonly commandBus: CommandBus, private readonly queryBus: QueryBus, ) {} @Post('command') async executeCommand(@Body() body: { commandType: string; payload: any }) { // 维护一个命令类型名到类的映射表,或者用下文的DI容器方式获取 const CommandClass = this.getCommandClass(body.commandType); const command = new CommandClass(body.payload); return this.commandBus.execute(command); } @Post('query') async executeQuery(@Body() body: { queryType: string; payload: any }) { const QueryClass = this.getQueryClass(body.queryType); const query = new QueryClass(body.payload); return this.queryBus.execute(query); } // 手动映射命令类,可根据项目结构调整 private getCommandClass(typeName: string): any { const commandMap = { CreateUserCommand: require('../commands/create-user.command').CreateUserCommand, // 这里加其他需要调试的命令 }; return commandMap[typeName]; } private getQueryClass(typeName: string): any { const queryMap = { GetUserQuery: require('../queries/get-user.query').GetUserQuery, // 这里加其他需要调试的查询 }; return queryMap[typeName]; } }
用Postman测试时,直接POST到/cqrs-debug/command或/cqrs-debug/query,Body里传对应的类型名和参数就能触发操作,不用再写一堆重复控制器。
二、优化:用DI容器自动映射命令/查询类
上面的手动映射表可以用NestJS的DI容器优化,不用每次加新命令都改映射:
import { Injectable, OnModuleInit } from '@nestjs/common'; import { CommandBus, QueryBus } from '@nestjs/cqrs'; import { ModuleRef } from '@nestjs/core'; @Injectable() export class CqrsDebugService implements OnModuleInit { private commandClasses = new Map<string, any>(); private queryClasses = new Map<string, any>(); constructor( private readonly moduleRef: ModuleRef, private readonly commandBus: CommandBus, private readonly queryBus: QueryBus, ) {} async onModuleInit() { // 从CommandBus自动提取已注册的命令类 this.commandBus['handlers'].forEach((handler) => { // 建议给命令类加静态属性commandName,比如CreateUserCommand.commandName = 'CreateUserCommand' const commandName = handler.constructor['commandName']; if (commandName) { this.commandClasses.set(commandName, handler.constructor); } }); // 同理自动提取查询类 this.queryBus['handlers'].forEach((handler) => { const queryName = handler.constructor['queryName']; if (queryName) { this.queryClasses.set(queryName, handler.constructor); } }); } getCommandClass(typeName: string) { return this.commandClasses.get(typeName); } getQueryClass(typeName: string) { return this.queryClasses.get(typeName); } }
把这个服务注入到通用控制器里,就能自动识别所有已注册的命令/查询,不用手动维护映射表。
三、动态向总线写入内容的工具
- 自定义CLI脚本
写个简单的Node脚本,直接初始化NestJS上下文,调用总线执行命令/查询:
const { NestFactory } = require('@nestjs/core'); const { AppModule } = require('./src/app.module'); async function run() { const app = await NestFactory.createApplicationContext(AppModule); const commandBus = app.get('CommandBus'); // 从命令行参数获取命令类型和参数 const commandType = process.argv[2]; const payload = JSON.parse(process.argv[3]); const CommandClass = app.get(commandType); const command = new CommandClass(payload); const result = await commandBus.execute(command); console.log('执行结果:', result); await app.close(); } run();
执行示例:node cqrs-debug-cli.js CreateUserCommand '{"name":"test","email":"test@example.com"}'
NestJS Console命令
用@nestjs/console包创建终端命令,直接在控制台触发操作,适合快速调试。Swagger UI扩展
给通用控制器加上Swagger注解,自动生成API文档,在Swagger UI里就能直接调试,不用Postman也能操作。
内容的提问来源于stack exchange,提问作者Alexy
相关产品推荐
相关产品推荐

