Skip to content
Download

CQRS e Sagas

O Bunstone fornece uma implementação completa do padrão Command Query Responsibility Segregation (CQRS).

Registro

Para usar os recursos de CQRS, você deve importar o CqrsModule no seu AppModule raiz. Como ele é um Módulo Global, os buses estarão disponíveis para injeção em todos os outros módulos.

typescript
import { Module, CqrsModule } from "@grupodiariodaregiao/bunstone";

@Module({
  imports: [CqrsModule],
})
export class AppModule {}

Command Bus

Commands são usados para executar ações que alteram o estado da aplicação.

typescript
// 1. Defina o Command
class CreateUserCommand implements ICommand {
  constructor(public readonly name: string) {}
}

// 2. Defina o Handler
@CommandHandler(CreateUserCommand)
class CreateUserHandler implements ICommandHandler<CreateUserCommand> {
  async execute(command: CreateUserCommand) {
    // lógica para criar usuário
    return { id: 1, name: command.name };
  }
}

// 3. Execute
const result = await commandBus.execute(new CreateUserCommand("John"));

Query Bus

Queries são usadas para recuperar dados sem alterar o estado.

typescript
@QueryHandler(GetUserQuery)
class GetUserHandler implements IQueryHandler<GetUserQuery> {
  async execute(query: GetUserQuery) {
    return { id: query.id, name: "John" };
  }
}

Event Bus

Events são usados para notificar outras partes do sistema de que algo aconteceu.

typescript
@EventsHandler(UserCreatedEvent)
class UserCreatedHandler implements IEventHandler<UserCreatedEvent> {
  handle(event: UserCreatedEvent) {
    console.log(`Usuário criado: ${event.userId}`);
  }
}

Sagas

Sagas são processos de longa duração que reagem a eventos e podem disparar novos commands. Elas usam uma abordagem de fluxo reativo.

typescript
@Injectable()
export class UserSaga {
  @Saga()
  onUserCreated = (events$: IEventStream) =>
    events$.pipe(
      ofType(UserCreatedEvent),
      map((event) => new SendWelcomeEmailCommand(event.email))
    );
}

Exemplo prático

Para um exemplo completo e funcional de CQRS com commands e handlers, veja:

ts
import {
  Module,
  Controller,
  Post,
  Body,
  AppStartup,
  CqrsModule,
  CommandBus,
  CommandHandler,
} from "../../index";

// 1. Define a Command
class CreateUserCommand {
  constructor(public readonly name: string) {}
}

// 2. Define a Command Handler
@CommandHandler(CreateUserCommand)
class CreateUserHandler {
  async execute(command: CreateUserCommand) {
    console.log(`Executing CreateUserCommand for name: ${command.name}`);
    return { id: "123", name: command.name };
  }
}

@Controller("users")
class UserController {
  constructor(private readonly commandBus: CommandBus) {}

  @Post()
  async createUser(@Body() body: { name: string }) {
    // 3. Dispatch the command via the Bus
    return await this.commandBus.execute(new CreateUserCommand(body.name));
  }
}

@Module({
  imports: [CqrsModule],
  controllers: [UserController],
  providers: [CreateUserHandler],
})
class AppModule {}

const app = await AppStartup.create(AppModule);
app.listen(3000, () => {
  console.log("CQRS example is running on http://localhost:3000");
});

Veja no GitHub

Distribuído sob a Licença MIT.