CQRS Explained With Nest JS - The Startup - Medium
CQRS Explained With Nest JS - The Startup - Medium
CQRS Explained With Nest JS - The Startup - Medium
By using Medium, you agree to our Privacy Policy, including cookie policy.
You have 2 free stories left this month. Sign up and get an extra one for free.
Renjith P Follow
Jan 22 · 8 min read
If you are not that much familiar with CQRS, please go through the link below given:
As I used to say before we start we have to know the benefits of onboarding CQRS
CQRS allows the read and write workloads to scale independently, and may result in
fewer lock contentions
The read side can use a schema that is optimized for queries, while the write side
uses a schema that is optimized for updates.
It’s easier to ensure that only the right domain entities are performing writes on the
data.
Segregating the read and write sides can result in models that are more
maintainable and flexible. Most of the complex business logic goes into the write
model. The read model can be relatively simple.
By storing a materialized view in the read database, the application can avoid
complex joins when querying.
application ow
We are using Nest JS as our framework that it has built-in support for CQRS.
First, you need to install Nest JS if it is not already done. Run npm i -g @nestjs/cli for
installation.
Now run nest new simple-cqrs-app to create the project. Once it is completed, get into
the project directory and run the project to see whether everything works fine.
cd simple-cqrs-app
npm run start
Then open your browser and navigate to http://localhost:3000/ . You will get a message
Now open src directory and create a folder “order” where we are going to put our order
related stuff. Then create a file order.events.ts and put the code like below:
) { }
constructor(
) { }
constructor(
) { }
As you know, since CQRS is developed for event-driven programming every module
within the application will be communicating via generating events. So here we have
mentioned 3 possible events that are related to the order module.
Then open app.controller.ts and replace the code with below given:
@Controller()
@Get()
this.eventBus.publish(
new OrderEvent(
Here QueryBus and EventBus are Observables. It means application modules can easily
subscribe to the whole stream and enrich communication between modules through
events.
When a user reaches app controller a new event for placing an order will be fired like
below given:
this.eventBus.publish(new OrderEvent(
orderTransactionGUID, 'Daniel Trimson', 'Samsung LED TV',
To make Medium work, we log user data. By using Medium, you agree to our Privacy Policy, including cookie policy.
50000),);
Let us see how this event ends up creating an order in the system.
Go to order directory and create a file order.command.ts and paste the code given
below:
constructor(
) { }
In order to make the application easier to understand, each change has to be preceded
by Command. When any command is dispatched, the application has to react to it.
Commands can be dispatched from the services (or directly from the
controllers/gateways) and consumed in corresponding Command Handlers.
Here we have defined a command that has to be dispatched when a customer places an
order. Let us see how it will be dispatched within the system.
For that create a file order.saga.ts and paste the code given below:
@Saga()
createOrder = (events$: Observable<any>): Observable<ICommand> => {
return events$.pipe(
ofType(OrderEvent),
}),
);
A saga can be thought of as a special event handler that returns commands. Sagas do
this by leveraging RxJS to receive and react to all events published to the event bus.
Followed by the OrderEvent saga will generate a OrderCommand and this will be handled
by a command handler. So we need to create a command handler and let it be
order.handler.ts having code given below:
@CommandHandler(OrderCommand)
export class OrderHandler implements ICommandHandler<OrderCommand> {
To make Medium work, we log user data. By using Medium, you agree to our Privacy Policy, including cookie policy.
constructor(
// tslint:disable-next-line:no-console
console.log(`Make a bid on ${orderItem}, with userID:
${orderUserGUID} amount: ${orderAmount}`);
await this.itemRepository.getItemById(orderItem),
);
item.commit();
with placing the order, the OrderHandler has to make an interaction with the item
model with which order has no direct relationship as per CQRS terms. So how do we
associate the model and the publisher? We need to use a publisher
mergeObjectContext() method inside our command handler.
So here we are using an instance of ItemRepository which will be having the item we are
looking for as the parameter of mergeObjectContext() to place the order.
Let us create the the item folder having files item.interface.ts , item.model.ts and
item.repository.ts .
id: string;
amount?: number;
super();
try {
// business logic
this.apply(new OrderEventSuccess(orderTransactionGUID,
this.item.id, amount, { email: '[email protected]', id: userID }));
} catch (e) {
}
}
To make Medium work, we log user data. By using Medium, you agree to our Privacy Policy, including cookie policy.
@Injectable()
};
async getAll() {
return [];
So ItemRepository will get the item for OrderHandler to complete the order like below:
item.commit();
Now everything works as expected. Notice that we need to commit() events since they're
not being dispatched immediately.
To makeSo once work,
Medium we log, user
commit() as we know
data. By using method
Medium, you agree
orderOnItem() to ourwill generate
Privacy any onecookie
Policy, including of thepolicy.
two
events — OrderEventSuccess or OrderEventFail.
So we need to handle the event in order to end the order placing cycle as we expected.
@Saga()
return events$.pipe(
ofType(OrderEventSuccess),
// tslint:disable-next-line:no-console
console.log('Order Placed')
return [];
}),
);
@Saga()
return events$.pipe(
ofType(OrderEventFail),
// tslint:disable-next-line:no-console
return [];
}),
);
}
To make Medium work, we log user data. By using Medium, you agree to our Privacy Policy, including cookie policy.
So if the order is a success you will get the “Order Placed” message in the console,
otherwise “Order Placing Failed”.
@Module({
imports: [CqrsModule],
controllers: [AppController],
providers: [
OrderHandler,
OrderSaga,
ItemRepository],
})
We are all set with our CQRS app. Below given will be your app structure now.
To make Medium work, we log user data. By using Medium, you agree to our Privacy Policy, including cookie policy.
Conclusion
Here we have done only the write part with CQRS. We will be adding the read side as
part 2 of this article because lengthy stories may be boring.
Happy Coding :)
Reference
CQRS: Nest JS
Nestjs work,
To make Medium JavaScript Software
we log user data. ByEngineering
using Medium,Software Development
you agree Programming
to our Privacy Policy, including cookie policy.