Implementing CQRS Pattern in NestJS

Ok, so now let’s dive into how we can implement this pattern in order to have a better understanding on how it works. We are going to do it by using NestJS Framework. NestJS provides us a pre-built CQRS Module, which is gonna make our life much easier in order to use this pattern with NestJS.

Firstly, let’s install NestJS’ CLI, create a new project with it and add Nest’s CQRS Module to our project.

npm install -g @nestjs/cli

nest new project-cqrs

cd project-cqrs

npm install @nestjs/cqrs --save

npm run start

For demonstration, we are going to implement a simple Users CRUD feature. We are going to create a User Controller in which we are going to use CQRS pattern. Let’s separate users as a feature, create a module for it, the controller itself and two folders: commands and queries. Then, we are going to create each one of the queries and commands we need.

Bellow is how our project structure and files are going to be.

We are going to dive into all of the users feature ones.

Queries

Let’s build our ListUsersQuery step by step as an example.

First, we define a class as our Query, that is going to contain the information needed by its Handler. Since we are going to list users, it is important to know the requested page number and page size.

export class ListUsersQuery {

constructor(

public page: number = 1,

public pageSize: number = 10

) { }

}

Next, we create our Handler, which is gonna be triggered when this Query is evoked in the QueryBus.

The QueryBus is a stream of queries. It delegates queries to its equivalent handlers when requested. Each Query must have its corresponding Handler. This association is achieved by using the @QueryHandler decorator.

@QueryHandler(ListUsersQuery)

export class ListHandler implements IQueryHandler<ListUsersQuery> {}

We’ve now created the ListHandler, which is going to handle the query ListUsersQuery that we had previously created. Later on we are going to see how this Query can be evoked by the QueryBus.

Now what we have got to do is actually have our logic inside our Handler. When the Query is evoked it is going to call the Handler’s execute method. There is were our Handler logic is going to reside.

@QueryHandler(ListUsersQuery)

export class ListHandler implements IQueryHandler<ListUsersQuery> { constructor(

// Here we would inject what is necessary to retrieve our data

) { } public async execute(query: ListUsersQuery): Promise<User[]> {

// Here we are going to have any necessary logic related

// to that Query and return the requested information

// such as a service method call

}

}

So finally this is how our ListUsersQuery is gonna look like:

export class ListQuery {

constructor(

public page: number = 1,

public pageSize: number = 10

) { }

} @QueryHandler(ListUsersQuery)

export class ListHandler implements IQueryHandler<ListUsersQuery> { constructor(

// Here we would inject what is necessary to retrieve our data

) { } public async execute(query: ListUsersQuery): Promise<User[]> {

// Here we are going to have any necessary logic related

// to that Query and return the request information

// such as a service method call

}

}

And below is how our other Query, the GetUserById one, would look like; now adding some example code accessing a repository using TypeORM to retrieve the data.

// All we need is the id of the user we want to retrieve the data

export class GetUserByIdQuery {

constructor(

public id: number

) { }

} @QueryHandler(GetUserByIdQuery)

export class GetUserByIdHandler implements

IQueryHandler<GetUserByIdQuery> { // We inject our TypeORM repository to fetch the user data

constructor(

@InjectRepository(User)

private readonly _repository: Repository<User>

) { } public async execute(query: GetUserByIdQuery): Promise<User> {

// We fetch user data and return it on the execute method

return await this._repository.findOne(query.id);

}

}

Commands

Let’s build our AddUserCommand step by step as an example.

It is all very similar to how we created our Queries.

First, we define a class as our Command, that is going to contain the information needed by its Handler. Here we will need the new user information to be added.

export class AddUserCommand {

constructor(

public name: string,

public email: string,

public birthdate: Date

) { }

}

Next, we create our Handler, which is gonna be triggered when this Command is evoked in the CommandBus.

The CommandBus is a stream of commands. It delegates commands to its equivalent handlers when requested. Each Command must have its corresponding Handler. This association is achieved by using the @CommandHandler decorator.

@CommandHandler(AddUserCommand)

export class AddUserHandler implements IQueryHandler<AddUserCommand> {}

We’ve now created the AddUserHandler, which is going to handle the command AddUserCommand that we had previously created. Later on we are going to see how this Command can be evoked by the CommandBus.

Now what we gotta do is actually have our logic inside our Handler. When the Command is evoked it is going to call the Handler’s execute method. There is were our Handler logic is going to reside.

@CommandHandler(AddUserCommand)

export class AddUserHandler implements IQueryHandler<AddUserCommand> {

constructor(

// Here we would inject what is necessary to persist our data

) { } public async execute(query: ListUsersQuery): Promise<User> {

// Here we are going to have any necessary logic related

// to that Command and do any change operations

}

}

So finally this is how our AddUserCommand is gonna look like:

export class AddUserCommand {

constructor(

public name: string,

public email: string,

public birthdate: Date

) { }

} @CommandHandler(AddUserCommand)

export class AddUserHandler implements IQueryHandler<AddUserCommand> {

constructor(

// Here we would inject what is necessary to persist our data

) { } public async execute(query: ListUsersQuery): Promise<User> {

// Here we are going to have any necessary logic related

// to that Command and do any change operations

}

}

And below is how our other Commands, UpdateUser and DeleteUser, would look like, now adding some example code accessing a repository using TypeORM to make changes to the database.

UpdateUserCommand

export class UpdateUserCommand {

constructor(

public id: number,

public name?: string,

public email?: string,

public birthdate?: Date

) { }

} @CommandHandler(UpdateUserCommand)

export class UpdateUserHandler implements IQueryHandler<UpdateUserCommand>

{

constructor(

@InjectRepository(User)

private readonly _repository: Repository<User>

) { } public async execute(request: UpdateUserCommand): Promise<User>

{

const user = await this._repository.findOne(request.id); if (!user)

throw new NotFoundException('User does not exist'); user.name = request.name || user.name;

user.email = request.email || user.email;

user.birthdate = request.birthdate|| user.birthdate; return await this._repository.save( user );

}

}

DeleteUserCommand

export class DeleteUserCommand {

constructor(

public id: number

) { }

} @CommandHandler(DeleteUserCommand)

export class DeleteUserHandler implements IQueryHandler<DeleteUserCommand>

{

constructor(

@InjectRepository(User)

private readonly _repository: Repository<User>

) { } public async execute(request: DeleteUserCommand):

Promise<DeleteResult>

{

return await this._repository.delete({

'id': request.id

});

}

}

Controller

Now that we have our Queries and Commands built we need to get it all together and create our UserController for our User CRUD endpoints.

First of all, we create our UserController class, using the @Controller decorator and specifying our ‘user’ route prefix for our controller. Then, we provide it the QueryBus and the CommandBus on the constructor.

@Controller('user')

export class UserController { constructor(

private readonly commandBus: CommandBus,

private readonly queryBus: QueryBus

) { } }

Now we are able to create our endpoints, which are going to invoke the QueryBus and the CommandBus accordingly.

@Controller('user')

export class UserController { constructor(

private readonly commandBus: CommandBus,

private readonly queryBus: QueryBus

) { } @Get()

public async getAllOngs(

@Query() request: ListUsersQuery,

@Res() response

) {



const users = await this.queryBus.execute(

new ListUsersQuery(

request.page,

request.pageSize

)

); response.status(HttpStatus.OK).json(users);

} }

We have defined the @Get, @Post, @Put and @Delete decorators to specify the wanted HTTP request method. Also, we have used the @Query decorator to retrieve the Query Params from the request, the @Param decorator to retrieve the Route Params from the request and the @Body decorator to retrieve the Request Body.

Then, all we had to do was invoke the QueryBus or the CommandBus accordingly to the endpoint purpose calling its execute method, providing the desired Query or Command and passing the necessary information. With the operation result, we can then return a response with the result of the operation and any data that is needed. And we are done!

Module

Now, all that is left to do is register it all in the UserModule. First we create an index file for the commands and another one to the queries. That will make it easier for visualizing it and providing it in the module. The index files are going to look like this:

// commands > _index.ts

export const CommandHandlers = [

AddUserHandler,

UpdateUserHandler,

DeleteUserHandler

]; // queries > _index.ts

export const QueryHandlers = [

ListHandler,

GetUserByIdHandler

];

Now we can create our UserModule, declare our UserController and provide our queries and commands. Then, we import it in our AppModule and we are ready to go!