design-patternsnestjscqrs

CQRS pattern, but Command function require some Query function


I got to separate the service into command and query. Some of the command methods require the methods from query. I initialized the query variable in the command's constructor. Here is the example in my nestjs app:

//command.ts
constructor(
    //...imports repos

    private salonServicesQuery: SalonServicesQuery //this one IMPORTED and be used later
  ) {
    //
  }

async updateService(serviceId: number, salonId: number, dto: UpdateServiceDto) {
    // do some cheking

    const service = await this.salonServicesQuery.getById(serviceId, salonId);// THIS one used to retrieve methods from the query class

    let category: any;
    if (dto.categoryId) {
      category = await this.categoryRepository.findOne({
        where: { id: dto.categoryId, salon: { id: salonId } },
      });

      if (!category) {
        throw new NotFoundException(
          `Category with ID '${dto.categoryId}' not found in salon '${salonId}'`
        );
      }
    }
   // continue doing logical things
}

My question is: am I doing right to separate them? Are there any ways/approachs that make more sense/better?

I imported query class in command class. I did not run the program bc I still have error with env but the vsc marked it all green and no error


Solution

  • If you are using CQRS, you must know that the databases for read and write should be separated. And the source of truth for validation is the write model. So the usage of the read model in command handlers is not recommended.

    If you need the same logic to get data in the write model (command handlers) just like the way it is defined in the query handlers, you should know that DRY doesn't apply here. You better duplicate your logic on both sides.

    The fact is we think we must reuse the modules (codes, functions, classes) to comply with DRY in any circumstances. But all situations aren't fit to apply DRY. For example, we try to apply DRY and reuse our queries in the specification pattern. But all sharings and reusabilities bring coupling. So as long as the coupling doesn't cost too much for us, we go with DRY.

    On the other hand, in CQRS you want to make the write and read model independent. CQRS is the way to make your architecture loosely coupled and by separating read and write concerns, it offers an opportunity to optimize and focus on each one independently. So the goal of CQRS is kind of the opposite of sharing the logic on both sides.

    Here is a good article by Vladimir Khorikov about CQRS and the specification pattern. It might be helpful.