Микросервисы с CQRS и Event Sourcing — это архитектура, которая в последние годы приобрела большую популярность. В этой статье мы рассмотрим, как создавать микросервисы с CQRS и Event Sourcing в TypeScript с использованием NestJS.
Прежде чем мы начнем, давайте разберемся, что такое CQRS и Event Sourcing:
CQRS (Command Query Responsibility Segregation) — это архитектурный шаблон, который отделяет операции записи (команды) от операций чтения (запросов). Это разделение позволяет по-разному оптимизировать операции чтения и записи, что приводит к повышению производительности и масштабируемости.
Источник событий — это способ хранения и извлечения данных путем сохранения всех изменений данных в виде последовательности событий. Вместо сохранения текущего состояния объекта мы сохраняем события, которые привели к текущему состоянию. Это позволяет нам восстановить состояние объекта в любой момент времени, воспроизводя события.
NestJS — это популярная платформа Node.js, которая обеспечивает структурированный и эффективный способ создания серверных приложений. Он построен на основе Express.js и предоставляет множество функций, таких как внедрение зависимостей, промежуточное программное обеспечение и многое другое.
Теперь давайте приступим к созданию микросервисов с помощью CQRS и Event Sourcing в TypeScript с использованием NestJS.
Создание микросервисов с безопасностью типов? Может быть хорошей идеей использовать набор инструментов с открытым исходным кодом, такой как Bit, для идентификации и изоляции компонентов в вашей кодовой базе, определяя их как независимые сущности, которые можно разрабатывать и тестировать по отдельности, а затем версионировать и документировать. , протестировано и опубликовано через центральный реестр. Подробнее здесь.
Шаг 1: Создайте новый проект NestJS Мы начнем с создания нового проекта NestJS с помощью интерфейса командной строки Nest. Откройте окно терминала и выполните следующую команду:
npm i -g @nestjs/cli nest new project-name
Шаг 2. Установите необходимые пакеты. Чтобы создать микросервисы с CQRS и Event Sourcing в NestJS, нам нужно установить некоторые необходимые пакеты. Откройте окно терминала в каталоге проекта и выполните следующую команду:
npm install @nestjs/microservices @nestjs/cqrs @nestjs/event-sourcing
Шаг 3. Создание микрослужбы Чтобы создать микрослужбу в NestJS, нам нужно определить контроллер, который будет обрабатывать входящие запросы и ответы. Создайте новый файл с именем app.controller.ts в каталоге src и добавьте следующий код:
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));
}
}
В этом коде мы определили контроллер, который прослушивает сообщения с помощью команды create-message. Когда сообщение получено, вызывается функция createMessage, которая создает новый CreateMessageCommand и выполняет его с помощью функции CommandBus.
Шаг 4: Создайте команду Теперь нам нужно создать команду, которая будет выполняться CommandBus. Создайте новый файл с именем create-message.command.ts в каталоге src/commands и добавьте следующий код:
import { ICommand } from '@nestjs/cqrs';
export class CreateMessageCommand implements ICommand {
constructor(public readonly text: string) {}
}
В этом коде мы определили CreateMessageCommand, реализующий интерфейс ICommand. Конструктор принимает параметр text, представляющий текст сообщения.
Шаг 5: Создайте событие. Когда команда выполняется, нам нужно создать событие, чтобы уведомить все заинтересованные стороны. Создайте новый файл с именем message-created.event.ts в каталоге src/events и добавьте следующий код:
import { IEvent } from '@nestjs/cq
import { IMessage } from '../interfaces/message.interface';
export class MessageCreatedEvent implements IEvent {
constructor(public readonly message: IMessage) {}
}
В этом коде мы определили MessageCreatedEvent, реализующий интерфейс IEvent. Конструктор принимает параметр message, который представляет созданное сообщение.
Шаг 6: Создайте хранилище событий Для хранения событий нам нужно создать хранилище событий. Создайте новый файл с именем `message.event-store.ts` в каталоге `src/event-stores` и добавьте следующий код:
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);
}
В этом коде мы определили хранилище событий, в котором события хранятся в виде массива. Мы также определили функцию с именем handleMessageCreatedEvent, которая вызывается, когда генерируется MessageCreatedEvent. Эта функция просто помещает событие в массив events.
Шаг 7: Создайте обработчик запросов Чтобы прочитать данные, нам нужно создать обработчик запросов. Создайте новый файл с именем message.query-handler.ts в каталоге src/query-handlers и добавьте следующий код:
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);
}
}
В этом коде мы определили обработчик запросов, который обрабатывает GetMessagesQuery. Функция execute возвращает массив сообщений, фильтруя массив events для событий MessageCreatedEvent и сопоставляя их со свойством сообщения.
Шаг 8: Создайте запрос Чтобы получить данные, нам нужно определить запрос. Создайте новый файл с именем get-messages.query.ts в каталоге src/queries и добавьте следующий код:
import { IQuery } from '@nestjs/cqrs';
export class GetMessagesQuery implements IQuery {}
В этом коде мы определили GetMessagesQuery, реализующий интерфейс IQuery.
Шаг 9: Определите интерфейс Чтобы определить структуру данных сообщения, нам нужно определить интерфейс. Создайте новый файл с именем message.interface.ts в каталоге src/interfaces и добавьте следующий код:
export interface IMessage {
id: number;
text: string;
}
В этом коде мы определили интерфейс IMessage, который имеет свойства id и text.
Шаг 10: Обновите файл app.module.ts Наконец, нам нужно обновить файл app.module.ts, чтобы зарегистрировать необходимых провайдеров и контроллеров. Добавьте следующий код в массивы providers и controllers:
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 {}
В этом коде мы импортировали Module, CommandBus, CqrsModule и QueryBus из @nestjs/common и @nestjs/cqrs. Мы также импортировали контроллеры, события, хранилище событий, обработчики запросов и запросы, которые создали ранее.
Мы определили класс AppModule и украсили его декоратором @Module. Мы импортировали CqrsModule и зарегистрировали его в массиве imports.
Мы зарегистрировали MessageController в массиве controllers.
Мы зарегистрировали CommandBus и QueryBus в качестве поставщиков со свойством useValue. Мы также зарегистрировали MessageEventStore, MessageCreatedEvent, GetMessagesQueryHandler и GetMessagesQuery в качестве поставщиков.
Используя свойства provide и useValue, мы указываем NestJS использовать экземпляры, которые мы создали для этих классов.
Обратите внимание, что мы не используем внедрение зависимостей для экземпляров CommandBus и QueryBus. Мы просто создаем новые экземпляры, используя ключевое слово new. Это потому, что у нас нет никаких зависимостей для этих классов.
Наконец, мы экспортируем класс AppModule.
Шаг 11. Протестируйте приложение
Чтобы протестировать приложение, выполните следующую команду:
npm run start:dev
Это запустит приложение NestJS в режиме разработки.
Чтобы создать новое сообщение, отправьте запрос POST на конечную точку `/messages` со следующей полезной нагрузкой:
{
"text": "Hello, world!"
}
Чтобы получить сообщения, отправьте запрос GET на конечную точку /messages.
Спасибо, что прочитали! Если вам понравилось читать этот пост, вы получили от него помощь, знания, вдохновение и мотивацию и хотите поддержать меня — вы можете Купить мне кофе. Ваша поддержка действительно имеет значение.
Поздравляем! Вы успешно внедрили микросервисную архитектуру с CQRS и источниками событий с помощью TypeScript и NestJS.
Создавайте приложения с повторно используемыми компонентами, как Lego

Инструмент с открытым исходным кодом Bit помогает более чем 250 000 разработчиков создавать приложения с компонентами.
Превратите любой пользовательский интерфейс, функцию или страницу в компонент многократного использования — и поделитесь им со своими приложениями. Легче сотрудничать и строить быстрее.
Разделите приложения на компоненты, чтобы упростить разработку приложений, и наслаждайтесь наилучшими возможностями для рабочих процессов, которые вы хотите: