Skip to content

Latest commit

 

History

History
105 lines (94 loc) · 5.48 KB

README-CQRS.md

File metadata and controls

105 lines (94 loc) · 5.48 KB

Hint: CQRS

  • Run process:
  • 1: controller 接收路由, 分辨是 CUD(create/update/delete)操作还是 R(read) 操作
  • 2.1: 如果是CUD操作, 调用 this.CommandBus.execute()
  • 2.2: 如果是R操作, 调用 this.QueryBus.execute()

Hint: 🚀🚀 Read

@Get()
async findAll(): Promise<Hero[]> {
    return this.queryBus.execute(new GetHeroesQuery());
}
  • 以查询R操作为例: 在 controller 里 执行 this.queryBus.execute(new GetHeroesQuery())
  • 3: 其中 GetHeroesQuery() 是 在 /queries/impl/ 文件夹中定义的 class
  • 4.1: 步骤[3]中的 class GetHeroesQuery/queries/handlers/ 下的 class GetHeroesHandler 中被引用
  • 4.2: 步骤[3]中的 class GetHeroesQuery 被 🎃🎃🎃 装饰器 @QueryHandler 🎃🎃🎃 作为参数使用, 并作用于 GetHeroesHandler 上 (依赖注入)
  • 4.3: 所以 当 步骤[2.2] 中调用 new GetHeroesQuery() 的时候 实际上执行的是 GetHeroesHandler 中的 execute 方法
  • 4.4: 步骤 [4.3] 中的 execute() 方法是由 系统的 🔐🔐🔐 IQueryHandler interface🔐🔐🔐 定义的, 必须实现
  • 5: 在 execute() 方法中 调用 repository 中的方法

Hint: 🚀🚀 CUD

@Post(':id/kill')
async killDragon(@Param('id') id: string, @Body() dto: KillDragonDto) {
    return this.commandBus.execute(new KillDragonCommand(id, dto.dragonId));
}
  • 以查询CUD操作为例: 在 controller 里 执行 this.commandBus.execute(new KillDragonCommand(id, dto.dragonId))
  • 6: 其中 KillDragonCommand() 是 在 /commands/impl/ 文件夹中定义的 class
  • 7.1: 步骤[6]中的 class KillDragonCommand/commands/handlers/ 下的 class KillDragonHandler 中被引用
  • 7.2: 步骤[6]中的 class KillDragonCommand 被 🎃🎃🎃 装饰器 @CommandHandler 🎃🎃🎃 作为参数使用, 并作用于 KillDragonHandler 上 (依赖注入)
  • 7.3: 所以 当 步骤[7.2] 中调用 new KillDragonCommand() 的时候 实际上执行的是 KillDragonHandler 中的 execute 方法
  • 7.4: 步骤 [7.3] 中的 execute() 方法是由 系统的 🔐🔐🔐ICommandHandler interface🔐🔐🔐 定义的, 必须实现
  • 8: 在 execute() 方法中 调用 EventPublisher 中 的实例对象publisher的方法 this.publisher.mergeObjectContext(), 并将 repository 中定义的方法作为传递进去
const hero = this.publisher.mergeObjectContext( // ?????????
    await this.repository.findOneById(+heroId),
);
hero.killEnemy(dragonId);
hero.commit();
  • 9: 返回的 hero对象具有 是/models/hero.model.ts中定义的 Hero类的实例
  • 10: 所以 hero具有 Hero 的 实例属性 id 和 实例方法 killEnemy
killEnemy(enemyId: string) {
    this.apply(new HeroKilledDragonEvent(this.id, enemyId));
}
  • 11: 当调用killEnemy的时候实际上 调用了/events/impl/下定义的class HeroKilledDragonEvent
  • 12.1 : 在步骤[11]中 class HeroKilledDragonEventevents/handlers下的 class HeroKilledDragonHandler中被引用
  • 12.2 : 步骤[11]中的 class HeroKilledDragonEvent 被 🎃🎃🎃 装饰器 @EventsHandler 🎃🎃🎃 作为参数使用, 并作用于 HeroKilledDragonHandler 上 (依赖注入)
  • 12.3: 所以 当 步骤[12.2] 中调用 new HeroKilledDragonEvent() 的时候 实际上执行的是 HeroKilledDragonHandler 中的 handle 方法
  • 12.4: 步骤 [12.3] 中的 handle() 方法是由 系统的 🔐🔐🔐 IEventHandler interface 🔐🔐🔐 定义的, 必须实现
    1. handle方法中再去使用repository做逻辑操作或者数据库操作

  • 先走 XXXX-command 或者 XXXX-query , 再走对应的 XXXX-handler

Pricinple

  • Dto: 作用于 参数返回值
  • impl: 接口类 对后续操作参数的定义
  • handler: 通过 装饰器 @CommandHandler 连接 controller -> impl -> handler的 依赖注入

Process again

  • controller中, 根据 CRUD 的类型, 选择走[CommandBus]还是[QueryBus]
  • 一般情况下执行 CommandBus 的操作,会带有参数,而这个参数大多数情况下就是 execute 参数中的 impl 中类的constructor 中定义的参数类型
// controller.js
this.commandBus.execute(new CreateHeroCommand(hero));

// /command/impl/create-hero.command.ts
import { HeroDto } from '../../interfaces/create-hero-dto';
export class CreateHeroCommand {
  constructor(public readonly hero: HeroDto) {}
}
  • CreateHeroCommand 这个类会在 /command/handler/create-hero.handler.ts 中 被装饰器@CommandHandler(CreateHeroCommand), 同时初始化 repository实例
  • 然后实现 接口类 ICommandHandler中的execute()方法, 并在内部调用repository的方法完成对数据的操作
  • 并且在 handler里的execute方法中使用的参数在 impl/xx-xx.command.ts类的构造函数中声明, 如下两段代码所示
// handler.ts
@CommandHandler(KillDragonCommand)
export class KillDragonHandler implements ICommandHandler<KillDragonCommand> {
  constructor(
    private readonly repository: HeroRepository,
  ) {}
  async execute(command: KillDragonCommand) {
    const { heroId, dragonId } = command;
  }
}
// command.ts
export class KillDragonCommand {
  constructor(public readonly heroId: string, public readonly dragonId: string) {}
}

cli-colo rules

  • handler: console.log(clc.greenBright('[command/handler]: xxx yyy handler'));
  • impl: console.log(clc.yellowBright('[command/impl]: aaa bbb command'));
  • repository: console.log(clc.bgCyanBright('[repository] => parameter', id));