간단한 CRUD (만들기, 읽기, 업데이트 및 삭제) 애플리케이션 의 흐름은 아래 단계로 사용하여설명 할 수 있습니다.

  1. 컨트롤러계층은 HTTP 서비스 계층에 대한 요청 및 Route작업을 처리합니다.
  2. 서비스 계층은 비즈니스 로직이 대부분입니다.
  3. 서비스는 저장소 / DAO를 사용하여 엔티티를 변경 / 유지합니다.
  4. 엔티티는 setter 및 getter와 함께 값의 컨테이너 역할을합니다.

대부분의 경우 중소 규모 애플리케이션의 경우이 패턴으로 충분합니다. 그러나 요구 사항이 더 복잡해지면 CQRS 모델이 더 적절하고 확장 가능할 수 있습니다. 이 모델을 용이하게하기 위해 Nest는 경량 CQRS 모듈을 제공 합니다 . 이 장에서는 사용 방법에 대해 설명합니다.

설치

$ npm install --save @nestjs/cqrs

Commands

이 모델안에서 Action은 Command라고 부릅니다. command가 dispatched하면 어플리케이션이 반응하게 됩니다. command는 서비스 계층이나 직접적으로 컨트롤러나 게이트웨이에서 dispatched 할 수 있습니다. command는 Command Handlers에서 사용됩니다.

heroes-game.service.ts

@Injectable()
export class HeroesGameService {
  constructor(private commandBus: CommandBus) {}

  async killDragon(heroId: string, killDragonDto: KillDragonDto) {
    return this.commandBus.execute(
      new KillDragonCommand(heroId, killDragonDto.dragonId)
    );
  }
}

위 코드는 KillDragonCommand을 dispatches한 샘플 서비스입니다. 다음 command 코드를 봐주세요.

kill-dragon.command.ts

export class KillDragonCommand {
  constructor(
    public readonly heroId: string,
    public readonly dragonId: string,
  ) {}
}

CommandBus는 commands을 stream 합니다. 이것은 commands에 대응하는 handlers에게 위임을 합니다. 각각의 command는 반드시 해당하는 Command Handler을 가지고 있습니다.

kill-dragon.handler.ts

@CommandHandler(KillDragonCommand)
export class KillDragonHandler implements ICommandHandler<KillDragonCommand> {
  constructor(private repository: HeroRepository) {}

  async execute(command: KillDragonCommand) {
    const { heroId, dragonId } = command;
    const hero = this.repository.findOneById(+heroId);

    hero.killEnemy(dragonId);
    await this.repository.persist(hero);
  }
}

이 접근 방식을 사용하면 모든 애플리케이션 상태 변경은 Command에 의한 occurrence으로부터 작동됩니다. 로직은 핸들러에 캡슐화됩니다. 이 접근 방식을 사용하면 로깅 또는 데이터베이스에 명령 유지 와 같은 동작을 간단히 추가 할 수 있습니다