Впровадження патерну CQRS у додатку NestJS

У світі архітектури програмного забезпечення, особливо в складних бізнес-додатках, підтримка чистого розділення команд і запитів може значно покращити масштабованість та підтримуваність вашої системи. І ось тут вступає в гру патерн Command Query Responsibility Segregation (CQRS). У цій статті ми розглянемо, як реалізувати патерн CQRS у додатку NestJS за допомогою TypeScript.

Що таке CQRS?

Патерн CQRS — це архітектурний патерн, який розділяє моделі для читання та запису даних. Простими словами, йдеться про те, щоб спроектувати додаток таким чином, що команди (які змінюють стан даних) і запити (які отримують дані) обробляються незалежно. Це розділення дозволяє оптимізувати продуктивність, безпеку, масштабованість і спрощує проектування складних додатків.

Чому NestJS?

NestJS — це прогресивний фреймворк для Node.js, який надає підтримку TypeScript з коробки і реалізує модульну архітектуру, що ідеально підходить для таких патернів, як CQRS. Завдяки своїй потужній системі Command та Query Bus, NestJS є ідеальним вибором для реалізації CQRS.

Налаштування проекту NestJS

Для початку вам потрібно створити базовий проект NestJS. Якщо ви ще не налаштували його, давайте створимо новий проект:

npm i -g @nestjs/cli  
nest new my-cqrs-app  
cd my-cqrs-app

Встановлення необхідних пакетів

NestJS надає спеціальний пакет для підтримки CQRS:

npm install @nestjs/cqrs

Реалізація CQRS у NestJS

Розглянемо патерн CQRS у NestJS на прикладі, де ми управляємо простим списком завдань. Ми створимо обробники як для команд, так і для запитів для завдань.

1. Визначення моделей команд і запитів

Спочатку потрібно визначити наші команди та запити. Це прості класи, які будуть служити об'єктами передачі даних (DTO).

// src/tasks/commands/impl/create-task.command.ts  
export class CreateTaskCommand {  
 constructor(  
 public readonly title: string,  
 public readonly description: string,  
 ) {}  
}  

// src/tasks/queries/impl/get-tasks.query.ts  
export class GetTasksQuery {}

2. Створення обробників команд

Обробники команд відповідають за виконання логіки, пов'язаної з командою. Давайте створимо обробник для CreateTaskCommand.

// src/tasks/commands/handlers/create-task.handler.ts  
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';  
import { CreateTaskCommand } from '../impl/create-task.command';  

@CommandHandler(CreateTaskCommand)  
export class CreateTaskHandler implements ICommandHandler {  
 async execute(command: CreateTaskCommand) {  
 const { title, description } = command;  
 // Логіка створення завдання  
 console.log(`Creating task: ${title}`);  
 // Збереження завдання в базу даних (псевдо-постійне збереження для прикладу)  
 return { title, description };  
 }  
}

3. Створення обробників запитів

Обробники запитів отримують дані з вашої системи. Давайте реалізуємо обробник для GetTasksQuery.

// src/tasks/queries/handlers/get-tasks.handler.ts  
import { IQueryHandler, QueryHandler } from '@nestjs/cqrs';  
import { GetTasksQuery } from '../impl/get-tasks.query';  

@QueryHandler(GetTasksQuery)  
export class GetTasksHandler implements IQueryHandler {  
 async execute(query: GetTasksQuery) {  
 // Логіка отримання завдань  
 console.log('Retrieving tasks');  
 // Отримання завдань з бази даних (псевдо-дані для прикладу)  
 return [{ title: 'Sample Task', description: 'This is a sample task' }];  
 }  
}

Реєстрація обробників та реалізація модуля

Переконайтесь, що ваші обробники правильно зареєстровані в модулі NestJS.

// src/tasks/tasks.module.ts
import { Module } from '@nestjs/common';
import { CqrsModule } from '@nestjs/cqrs';
import { CreateTaskHandler } from './commands/handlers/create-task.handler';
import { GetTasksHandler } from './queries/handlers/get-tasks.handler';

@Module({
imports: [CqrsModule],
providers: [CreateTaskHandler, GetTasksHandler],
})
export class TasksModule {}
```

5. Надсилання команд і запитів

Нарешті, інжектуємо CommandBus та QueryBus, щоб відправляти команди та запити з контролерів або сервісів.

// src/tasks/tasks.service.ts  
import { Injectable } from '@nestjs/common';  
import { CommandBus, QueryBus } from '@nestjs/cqrs';  
import { CreateTaskCommand } from './commands/impl/create-task.command';  
import { GetTasksQuery } from './queries/impl/get-tasks.query';  

@Injectable()  
export class TasksService {  
 constructor(  
 private readonly commandBus: CommandBus,  
 private readonly queryBus: QueryBus,  
 ) {}  

 async createTask(title: string, description: string) {  
 return this.commandBus.execute(new CreateTaskCommand(title, description));  
 }  

 async getTasks() {  
 return this.queryBus.execute(new GetTasksQuery());  
 }  
}

Висновок

Реалізація патерну CQRS за допомогою NestJS надає дисциплінований підхід до розділення обов'язків у вашому додатку. Хоча ця стаття надає базове розуміння, важливо врахувати наслідки відкладеної узгодженості в розподілених системах, які часто супроводжують реалізацію CQRS.

Для подальшого вивчення вам може бути цікаво інтегрувати Event Sourcing з CQRS, оскільки ці патерни часто доповнюють один одного, особливо в складних і високо масштабованих системах.

Тепер, коли ви маєте основи, починайте створювати чисті, надійні та високо масштабовані додатки, використовуючи CQRS з NestJS! Бажаю успіху в кодуванні!

Перекладено з: Implementing the CQRS Pattern in a NestJS Application

Leave a Reply

Your email address will not be published. Required fields are marked *