CQRS trong NestJS là gì?

Dùng mẫu CQRS hợp lý, bạn có thể tạo ra những app NestJS sạch và dễ mở rộng hơn.

CQRS trong NestJS

Một phương pháp tiếp cận lập trình NestJS phổ biến là xây dựng các dịch vụ điều khiển giao tiếp để truy cập dữ liệu. Thế nhưng, hình thức này không phải là mẫu thiết kế hợp lệ duy nhất trong NestJS. Bạn còn có nhiều mẫu khác, trong đó bao gồm CQRS.

CQRS là một mẫu thiết kế tách riêng các hoạt động đọc và viết của một ứng dụng. Sự phân tách này có thể giúp cải thiện khả năng mở rộng, hiệu suất và khả năng bảo trì.

Dưới đây là mọi điều bạn cần biết về CQRS và cách áp dụng nó khi xây dựng một NestJS API.

CQRS là gì?

CQRS là chữ viết tắt của command-query responsibility segregation. Nó dùng các lệnh để tạo, update, xóa dữ liệu và các truy vấn để tìm nạp dữ liệu. Điều này giúp loại bỏ nhu cầu triển khai các lệnh gọi cơ sở dữ liệu của ứng dụng vào dịch vụ.

Nó cũng cho thấy sự phân biệt rõ ràng giữa logic truy vấn database cho dữ liệu và thực hiện các tác vụ khác trong ứng dụng.

Phương pháp CQRS hữu ích trong thiết kế hướng tên miền. Điều này cho phép bạn tách riêng các hoạt động liên quan tới cơ sở hạ tầng và logic domain trong ứng dụng. Bạn cũng có thể dùng nó để triển khai logic nghiệp vụ phức tạp, nhưng không nên áp dụng cho những app đơn giản hơn.

Dùng CQRS trong NestJS API

Bạn có thể dùng mẫu thiết kế CQRS trong một API đã dựng ở NestJS. Để tiếp tục, bạn cần cài Node.js trên máy tính và phiên bản NestJS mới nhất.

Giờ làm theo những bước, bạn sẽ tạo được một app viết blog đơn giản bằng mẫu thiết kế CQRS.

Tạo một dự án Nest

Tạp một dự án Nest mới cùng một nguồn post cho ứng dụng blog. Bạn có thể làm việc này bằng cách chạy lệnh sau trong terminal:

nest new nestjs-cqrs
nest g module posts
nest g controller posts
nest g service posts

Cài đặt các phần phụ thuộc

Sau khi đã hoàn thành các bước kể trên, chạy lệnh terminal này để cài gói NestJS CQRS:

npm install --save @nestjs/cqrs

Tạo một dịch vụ chuyển phát

Thêm code sau vào file posts.service.ts để xác định class PostService.

// posts.service.ts
import { Injectable } from '@nestjs/common';

export interface Post {
    title: string;
    content: string;
}

@Injectable()
export class PostService {
  private readonly posts: Post[] = [];

  create(post: Post): Post {
    this.posts.push(post);
    return post;
  }

  findById(id: number): Post {
    return this.posts.find(post => post.id === id);
  }
}

PostService định nghĩa phương thức createfindById để tạo một bài đăng mới, đồng thời lấy bài đăng hiện tại từ ID của nó.

Định nghĩa các lệnh và truy vấn

Bước tiếp theo để định nghĩa truy vấn và lệnh là cốt lõi của mẫu thiết kế CQRS.

Trong thư mục posts, tạo hai file mới: createPostCommand.command.ts & getPostQuery.query.ts. File lệnh này trông sẽ như sau:

// createPostCommand.command.ts
export class CreatePostCommand {
  constructor(public readonly title: string, public readonly content: string) {}
}

Và file định nghĩ truy vấn trông sẽ như thế này:

// getPostQuery.query.ts
export class GetPostQuery {
  constructor(public readonly id: number) {}
}

Tạo lệnh và trình xử lý truy vấn

Sau khi thành công định nghĩa lệnh & truy vấn, bạn cần tạo các trình xử lý cho chúng. Một trình xử lý là một hàm chạy lệnh hoặc truy vấn, rồi trả về kết quả.

Tạo file handlers.ts trong thư mục post và dán code sau vào bên trong nó:

// handlers.ts
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
import { CreatePostCommand } from './createPostCommand.command.ts';
import { PostService } from './post.service';

@CommandHandler(CreatePostCommand)
export class CreatePostHandler implements ICommandHandler<CreatePostCommand> {
  constructor(private readonly postService: PostService) {}

  async execute(command: CreatePostCommand) {
    const { name, price } = command;
    const post = await this.postService.create(title, content);
    return post;
  }
}

Trong cùng file handlers.ts, bạn có thể chỉnh sửa các lệnh nhập để bao gồm code như bên dưới và cho phép làm việc với các truy vấn. Sau đó, bạn có thể chạy trình xử lý truy vấn như code bên dưới:

// handler.ts
import { QueryHandler, IQueryHandler } from '@nestjs/cqrs';
import { GetPostQuery } from './getPostQuery.query';
import { PostService } from './post.service';

// query handler
@QueryHandler(GetProductQuery)
export class GetPostHandler implements IQueryHandler<GetPostQuery> {
  constructor(private readonly postService: PostService) {}

  async execute(query: GetPostQuery) {
    const { id } = query;
    const post = await this.postService.findOneById(id);
    return post;
  }
}

Trình xử lý Register

Bước cuối cùng là đăng ký lệnh và trình xử lý truy vấn bằng mô đun NestJS.

// post.module.ts
import { Module } from '@nestjs/common';
import { CommandHandlers, QueryHandlers } from 'handlers.ts';
import { PostService } from './post.service';

@Module({
  providers: [
    PostService,
    ...CommandHandlers,
    ...QueryHandlers,
  ],
})
export class PostModule {}

Code này đăng ký PostService, CommandHandlers, QueryHandlers trong mảng providers. Dùng toán tử mở rộng (…) để hợp nhất các mảng của trình xử lý querycommand vào mảng providers.

Chạy lệnh và truy vấn

Các lệnh đã đăng ký cùng trình xử lý truy vấn có thể được dùng ở những controllers. Code sau triển khai một trình điều khiển post chấp nhận các truy vấn HTTP và trả về phản hồi cần thiết.

// posts.controller.ts
import { Body, Controller, Post } from '@nestjs/common';
import { CommandBus } from '@nestjs/cqrs';
import { CreatePostCommand } from './createPostCommand.command.ts';

// controller that implements command
@Controller('posts')
export class PostController {
  constructor(private readonly commandBus: CommandBus) {}

  @Post()
  async createPost(@Body() body: { title: string; content: string }) {
    const { title, content } = body;
    const command = new CreatePostCommand(title, content);
    const post = await this.commandBus.execute(command);
    return post;
  }
}

Ở code trên, CommandBus chạy CreatePostCommand và tạo một bài đăng mới. Code này hiện cách triển khai một controller dùng truy vấn:

// posts.controller.ts
import { Controller, Get, Param } from '@nestjs/common';
import { QueryBus } from '@nestjs/cqrs';
import { GetPostQuery } from './getPostQuery.query';

@Controller('posts')
export class PostController {
  constructor(private readonly queryBus: QueryBus) {}

  @Get(':id')
  async getPost(@Param('id') id: number) {
    const query = new GetPostQuery(id);
    const post = await this.queryBus.execute(query);
    return post;
  }
}

queryBus chạy GetPostQuery để nhận bài đăng chứa ID được cung cấp và trả về nó.

Sau khi hoàn thành tất cả các bước trên, bạn sẽ có một ứng dụng hoạt động đơn giản trong việc tạo & tìm nạp các bài viết trên blog.

Trên đây là tất cả những điều bạn cần biết về mẫu thiết kế CQRS. Hi vọng bài viết hữu ích với các bạn.

Thứ Năm, 23/03/2023 17:02
2,25 👨 444
0 Bình luận
Sắp xếp theo