This content originally appeared on Bits and Pieces - Medium and was authored by Suneel Kumar
Microservices with CQRS and Event Sourcing is an architecture that has gained a lot of popularity in recent years. In this article, we will explore how to build microservices with CQRS and Event Sourcing in TypeScript using NestJS.
Before we begin, let’s understand what CQRS and Event Sourcing are:
CQRS (Command Query Responsibility Segregation) is an architectural pattern that separates the write operations (commands) from the read operations (queries). This separation allows us to optimize the read and write operations differently, resulting in better performance and scalability.
Event Sourcing is a way of storing and retrieving data by persisting all changes to the data as a sequence of events. Rather than storing the current state of an entity, we store the events that led to the current state. This allows us to rebuild the state of an entity at any point in time by replaying the events.
NestJS is a popular Node.js framework that provides a structured and efficient way to build server-side applications. It is built on top of Express.js and provides many features such as dependency injection, middleware, and more.
Now, let’s get started with building microservices with CQRS and Event Sourcing in TypeScript using NestJS.
Building microservices with type-safety? It might be a good idea to use an open-source toolchain like Bit to identify and isolate components within your codebase, defining them as independent entities that can be developed and tested separately, then versioned, documented, tested, and shared via a central registry. Learn more here.
Step 1: Create a new NestJS project We will start by creating a new NestJS project using the Nest CLI. Open a terminal window and run the following command:
npm i -g @nestjs/cli
nest new project-name
Step 2: Install necessary packages To build microservices with CQRS and Event Sourcing in NestJS, we need to install some necessary packages. Open a terminal window in the project directory and run the following command:
npm install @nestjs/microservices @nestjs/cqrs @nestjs/event-sourcing
Step 3: Create a microservice To create a microservice in NestJS, we need to define a controller that will handle incoming requests and responses. Create a new file named app.controller.ts in the src directory and add the following code:
import { Controller } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices';
import { CommandBus } from '@nestjs/cqrs';
import { CreateMessageCommand } from './commands/create-message.command';
@Controller()
export class AppController {
constructor(private readonly commandBus: CommandBus) {}
@MessagePattern({ cmd: 'create-message' })
async createMessage(data: { text: string }) {
return this.commandBus.execute(new CreateMessageCommand(data.text));
}
}
In this code, we have defined a controller that listens to messages with the create-message command. When a message is received, the createMessage function is called, which creates a new CreateMessageCommand and executes it using the CommandBus.
Step 4: Create a command We now need to create a command that will be executed by the CommandBus. Create a new file named create-message.command.ts in the src/commands directory and add the following code:
import { ICommand } from '@nestjs/cqrs';
export class CreateMessageCommand implements ICommand {
constructor(public readonly text: string) {}
}
In this code, we have defined a CreateMessageCommand that implements the ICommand interface. The constructor takes a text parameter, which represents the message text.
Step 5: Create an event When a command is executed, we need to emit an event to notify any interested parties. Create a new file named message-created.event.ts in the src/events directory and add the following code:
import { IEvent } from '@nestjs/cq
import { IMessage } from '../interfaces/message.interface';
export class MessageCreatedEvent implements IEvent {
constructor(public readonly message: IMessage) {}
}
In this code, we have defined a `MessageCreatedEvent` that implements the `IEvent` interface. The constructor takes a `message` parameter, which represents the message that was created.
Step 6: Create an event store To store the events, we need to create an event store. Create a new file named `message.event-store.ts` in the `src/event-stores` directory and add the following code:
import { Injectable } from '@nestjs/common';
import { IEvent } from '@nestjs/cqrs';
import { EventStore } from '@nestjs/event-sourcing';
import { MessageCreatedEvent } from '../events/message-created.event';
@Injectable()
export class MessageEventStore {
@EventStore()
events: IEvent[];
handleMessageCreatedEvent(event: MessageCreatedEvent) {
this.events.push(event);
}
In this code, we have defined an event store that stores the events in an array. We have also defined a function named handleMessageCreatedEvent that is called when a MessageCreatedEvent is emitted. This function simply pushes the event to the events array.
Step 7: Create a query handler To read the data, we need to create a query handler. Create a new file named message.query-handler.ts in the src/query-handlers directory and add the following code:
import { IQueryHandler, QueryHandler } from '@nestjs/cqrs';
import { IMessage } from '../interfaces/message.interface';
import { GetMessagesQuery } from './queries/get-messages.query';
import { MessageEventStore } from '../event-stores/message.event-store';
@QueryHandler(GetMessagesQuery)
export class GetMessagesQueryHandler implements IQueryHandler<GetMessagesQuery> {
constructor(private readonly messageEventStore: MessageEventStore) {}
async execute(query: GetMessagesQuery): Promise<IMessage[]> {
const events = this.messageEventStore.events.filter(event => event instanceof MessageCreatedEvent) as MessageCreatedEvent[];
return events.map(event => event.message);
}
}
In this code, we have defined a query handler that handles GetMessagesQuery. The execute function returns an array of messages by filtering the events array for MessageCreatedEvent events and mapping them to the message property.
Step 8: Create a query To retrieve the data, we need to define a query. Create a new file named get-messages.query.ts in the src/queries directory and add the following code:
import { IQuery } from '@nestjs/cqrs';
export class GetMessagesQuery implements IQuery {}
In this code, we have defined a GetMessagesQuery that implements the IQuery interface.
Step 9: Define an interface To define the structure of the message data, we need to define an interface. Create a new file named message.interface.ts in the src/interfaces directory and add the following code:
export interface IMessage {
id: number;
text: string;
}
In this code, we have defined an IMessage interface that has an id and text property.
Step 10: Update the app.module.ts file Finally, we need to update the app.module.ts file to register the necessary providers and controllers. Add the following code to the providers and controllers arrays:
import { Module } from '@nestjs/common';
import { CommandBus, CqrsModule, QueryBus } from '@nestjs/cqrs';
import { MessageController } from './controllers/message.controller';
import { MessageCreatedEvent } from './events/message-created.event';
import { MessageEventStore } from './event-stores/message.event-store';
import { GetMessagesQueryHandler } from './query-handlers/get-messages.query-handler';
import { GetMessagesQuery } from './queries/get-messages.query';
@Module({
imports: [CqrsModule],
controllers: [MessageController],
providers: [
{
provide: CommandBus,
useValue: new CommandBus(),
},
{
provide: QueryBus,
useValue: new QueryBus(),
},
MessageEventStore,
MessageCreatedEvent,
GetMessagesQueryHandler,
GetMessagesQuery,
],
})
export class AppModule {}
In this code, we have imported Module, CommandBus, CqrsModule, and QueryBus from @nestjs/common and @nestjs/cqrs. We have also imported the controllers, events, event store, query handlers, and queries that we created earlier.
We have defined an AppModule class and decorated it with the @Module decorator. We have imported the CqrsModule and registered it in the imports array.
We have registered the MessageController in the controllers array.
We have registered the CommandBus and QueryBus as providers with the useValue property. We have also registered the MessageEventStore, MessageCreatedEvent, GetMessagesQueryHandler, and GetMessagesQuery as providers.
By using the provide and useValue properties, we are telling NestJS to use the instances that we have created for these classes.
Note that we are not using dependency injection for the CommandBus and QueryBus instances. We are simply creating new instances using the new keyword. This is because we don't have any dependencies for these classes.
Finally, we export the AppModule class.
Step 11: Test the application
To test the application, run the following command:
npm run start:dev
This will start the NestJS application in development mode.
To create a new message, send a POST request to the `/messages` endpoint with the following payload:
{
"text": "Hello, world!"
}
To retrieve the messages, send a GET request to the /messages endpoint.
Congratulations! You have successfully implemented a microservice architecture with CQRS and event sourcing using TypeScript and NestJS.
Build Apps with reusable components, just like Lego
Bit’s open-source tool help 250,000+ devs to build apps with components.
Turn any UI, feature, or page into a reusable component — and share it across your applications. It’s easier to collaborate and build faster.
Split apps into components to make app development easier, and enjoy the best experience for the workflows you want:
→ Micro-Frontends
→ Design System
→ Code-Sharing and reuse
→ Monorepo
Learn more:
- How We Build Micro Frontends
- How we Build a Component Design System
- How to reuse React components across your projects
- 5 Ways to Build a React Monorepo
- How to Create a Composable React App with Bit
Microservices with CQRS and Event Sourcing in TypeScript with NestJS was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.
This content originally appeared on Bits and Pieces - Medium and was authored by Suneel Kumar
Suneel Kumar | Sciencx (2023-02-21T12:48:10+00:00) Microservices with CQRS and Event Sourcing in TypeScript with NestJS. Retrieved from https://www.scien.cx/2023/02/21/microservices-with-cqrs-and-event-sourcing-in-typescript-with-nestjs-2/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.