Microservices với NestJS năm 2026: Kiến trúc, gRPC và Câu hỏi Phỏng vấn

Hướng dẫn toàn diện về kiến trúc microservices NestJS với gRPC: transport layer, Protocol Buffers, streaming patterns và câu hỏi phỏng vấn cho backend engineer năm 2026.

Kiến trúc NestJS Microservices với gRPC

Microservices đã trở thành kiến trúc phổ biến cho các ứng dụng doanh nghiệp quy mô lớn, và NestJS cung cấp một bộ công cụ mạnh mẽ để xây dựng hệ thống microservices hiệu quả. Trong năm 2026, việc hiểu rõ cách thiết kế và triển khai microservices với NestJS không chỉ giúp các nhà phát triển xây dựng ứng dụng có khả năng mở rộng cao mà còn là kiến thức quan trọng trong các buổi phỏng vấn kỹ thuật. Bài viết này sẽ khám phá kiến trúc transport layer, gRPC integration, các mẫu thiết kế đáng tin cậy và những câu hỏi phỏng vấn thường gặp về NestJS microservices.

Tại sao chọn gRPC thay vì REST cho microservices?

gRPC sử dụng Protocol Buffers với binary serialization, mang lại hiệu suất cao hơn đáng kể so với JSON trong REST. Ngoài ra, gRPC hỗ trợ streaming hai chiều, contract-first development với file .proto, và tự động generate client/server code cho nhiều ngôn ngữ lập trình khác nhau.

Kiến trúc Transport Layer trong NestJS Microservices

NestJS cung cấp hai decorator chính để xử lý giao tiếp giữa các microservices: @MessagePattern@EventPattern. Việc hiểu rõ sự khác biệt giữa hai decorator này là nền tảng quan trọng khi thiết kế hệ thống microservices.

@MessagePattern được sử dụng cho mô hình request-response, nơi service gửi yêu cầu sẽ chờ đợi phản hồi từ service nhận. Ngược lại, @EventPattern dành cho mô hình fire-and-forget, nơi service phát sự kiện không cần chờ phản hồi.

orders.controller.tstypescript
import { Controller } from '@nestjs/common';
import { MessagePattern, EventPattern, Payload } from '@nestjs/microservices';
import { OrdersService } from './orders.service';
import { CreateOrderDto } from './dto/create-order.dto';

@Controller()
export class OrdersController {
  constructor(private readonly ordersService: OrdersService) {}

  // Request-response: caller waits for the created order
  @MessagePattern('order.create')
  async createOrder(@Payload() data: CreateOrderDto) {
    return this.ordersService.create(data);
  }

  // Event-based: fire and forget, no response returned
  @EventPattern('order.shipped')
  async handleOrderShipped(@Payload() data: { orderId: string }) {
    await this.ordersService.markAsShipped(data.orderId);
  }
}

Trong NestJS 11, phương thức unwrap() được giới thiệu để xử lý response từ microservices một cách rõ ràng hơn. Phương thức này cho phép các nhà phát triển truy cập trực tiếp vào dữ liệu phản hồi mà không cần xử lý Observable wrapper, giúp code trở nên gọn gàng và dễ đọc hơn trong các tình huống đồng bộ.

Cấu hình gRPC làm Transport cho NestJS Microservice

gRPC là một framework RPC hiệu suất cao được phát triển bởi Google, sử dụng Protocol Buffers làm Interface Definition Language (IDL). Để tích hợp gRPC vào NestJS microservice, các nhà phát triển cần định nghĩa contract thông qua file .proto.

proto/users.protoprotobuf
syntax = "proto3";

package users;

service UsersService {
  rpc FindOne (UserById) returns (User);
  rpc FindMany (UserFilter) returns (stream User);
}

message UserById {
  string id = 1;
}

message UserFilter {
  string role = 1;
  int32 limit = 2;
}

message User {
  string id = 1;
  string email = 2;
  string name = 3;
  string role = 4;
}

File proto định nghĩa service UsersService với hai phương thức: FindOne trả về một user đơn lẻ và FindMany sử dụng server streaming để trả về nhiều users. Các message types như UserById, UserFilterUser xác định cấu trúc dữ liệu được trao đổi.

Tiếp theo, cấu hình bootstrap cho gRPC microservice trong file main.ts:

main.tstypescript
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { join } from 'path';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.createMicroservice<MicroserviceOptions>(
    AppModule,
    {
      transport: Transport.GRPC,
      options: {
        package: 'users',
        protoPath: join(__dirname, 'proto/users.proto'),
        url: '0.0.0.0:5000',
      },
    },
  );
  await app.listen();
}
bootstrap();

Controller xử lý các gRPC method calls sử dụng decorator @GrpcMethod:

users.controller.tstypescript
import { Controller } from '@nestjs/common';
import { GrpcMethod } from '@nestjs/microservices';
import { UsersService } from './users.service';

@Controller()
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @GrpcMethod('UsersService', 'FindOne')
  async findOne(data: { id: string }) {
    return this.usersService.findById(data.id);
  }
}

Các mẫu Streaming trong gRPC với NestJS

gRPC hỗ trợ bốn mẫu giao tiếp: unary (request-response đơn lẻ), server streaming, client streaming và bidirectional streaming. NestJS tích hợp tốt với RxJS để xử lý các mẫu streaming này một cách reactive.

Server streaming cho phép server gửi nhiều response cho một request đơn lẻ. Điều này hữu ích khi cần trả về tập dữ liệu lớn hoặc cập nhật real-time:

users.controller.ts — server streamingtypescript
import { Observable, from } from 'rxjs';
import { map } from 'rxjs/operators';
import { GrpcMethod } from '@nestjs/microservices';

@GrpcMethod('UsersService', 'FindMany')
findMany(data: { role: string; limit: number }): Observable<any> {
  // Stream users matching the filter one by one
  const users$ = from(this.usersService.findByRole(data.role, data.limit));
  return users$.pipe(
    map((user) => ({
      id: user.id,
      email: user.email,
      name: user.name,
      role: user.role,
    })),
  );
}

Bidirectional streaming cho phép cả client và server gửi và nhận dữ liệu đồng thời thông qua một kết nối persistent. Mẫu này phù hợp cho các ứng dụng chat real-time, collaborative editing hoặc game multiplayer.

Xử lý lỗi trong Streaming

Khi làm việc với streaming, việc xử lý lỗi cần được quan tâm đặc biệt. Nếu một lỗi xảy ra giữa stream, toàn bộ stream sẽ bị đóng. Các nhà phát triển nên sử dụng operator catchError của RxJS để xử lý lỗi gracefully và cân nhắc implement retry logic với exponential backoff cho các trường hợp lỗi tạm thời.

Ứng dụng Hybrid: HTTP và gRPC trên cùng một Service

Trong nhiều tình huống thực tế, một service cần phục vụ cả HTTP requests từ frontend và gRPC calls từ các microservices khác. NestJS hỗ trợ hybrid applications cho phép chạy đồng thời nhiều transport layers.

main.ts — hybrid applicationtypescript
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { join } from 'path';
import { AppModule } from './app.module';

async function bootstrap() {
  // HTTP server on port 3000
  const app = await NestFactory.create(AppModule);

  // gRPC microservice on port 5000
  app.connectMicroservice<MicroserviceOptions>({
    transport: Transport.GRPC,
    options: {
      package: 'users',
      protoPath: join(__dirname, 'proto/users.proto'),
      url: '0.0.0.0:5000',
    },
  });

  await app.startAllMicroservices();
  await app.listen(3000);
}
bootstrap();

Cấu hình này cho phép service lắng nghe HTTP requests trên port 3000 đồng thời xử lý gRPC calls trên port 5000. Các controllers có thể sử dụng cả HTTP decorators (@Get, @Post) và gRPC decorators (@GrpcMethod) trong cùng một ứng dụng.

Sẵn sàng chinh phục phỏng vấn Node.js / NestJS?

Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.

Ranh giới Service và Domain-Driven Design với NestJS

Khi thiết kế kiến trúc microservices, việc xác định ranh giới giữa các services là một trong những quyết định quan trọng nhất. Domain-Driven Design (DDD) cung cấp các nguyên tắc hữu ích để phân chia bounded contexts.

Mỗi microservice nên sở hữu một bounded context riêng với database độc lập. Trong NestJS, modules đóng vai trò như các building blocks tự nhiên cho việc tổ chức domain logic. Các nhà phát triển nên nhóm các entities, repositories, services và controllers liên quan vào cùng một module.

Aggregates trong DDD tương ứng với các entity clusters được truy cập thông qua một aggregate root. Khi một service cần dữ liệu từ domain khác, nó nên giao tiếp thông qua well-defined interfaces (gRPC methods hoặc message patterns) thay vì truy cập trực tiếp database.

Anti-Corruption Layer (ACL) là một pattern quan trọng khi tích hợp với external systems hoặc legacy services. Trong NestJS, ACL có thể được implement như một separate module với adapters chuyển đổi giữa external models và internal domain models.

Các mẫu Reliability: Timeouts, Retries và Circuit Breakers

Trong môi trường distributed systems, failures là điều không thể tránh khỏi. Các mẫu reliability giúp hệ thống xử lý lỗi một cách graceful và duy trì tính khả dụng.

orders.service.tstypescript
import { Inject, Injectable } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { firstValueFrom, timeout, retry } from 'rxjs';

@Injectable()
export class OrdersService {
  private usersService: any;

  constructor(@Inject('USERS_PACKAGE') private client: ClientGrpc) {}

  onModuleInit() {
    this.usersService = this.client.getService('UsersService');
  }

  async getOrderWithUser(orderId: string, userId: string) {
    // 3-second deadline, 2 retries with exponential backoff
    const user = await firstValueFrom(
      this.usersService.findOne({ id: userId }).pipe(
        timeout(3000),
        retry({ count: 2, delay: (err, retryCount) => {
          const jitter = Math.random() * 100;
          return new Promise(r => setTimeout(r, 1000 * retryCount + jitter));
        }}),
      ),
    );
    return { orderId, user };
  }
}

Timeout đặt giới hạn thời gian tối đa cho một request. Trong ví dụ trên, nếu gRPC call không hoàn thành trong 3 giây, một error sẽ được throw.

Retry with exponential backoff tự động thử lại request khi gặp lỗi tạm thời. Jitter được thêm vào để tránh thundering herd problem khi nhiều clients cùng retry đồng thời.

Circuit Breaker ngăn chặn cascade failures bằng cách "mở circuit" khi số lượng failures vượt quá ngưỡng. Trong trạng thái open, tất cả requests sẽ fail immediately mà không cần gọi downstream service. Sau một khoảng thời gian, circuit chuyển sang half-open state để thử nghiệm xem service đã recovery chưa.

Các thư viện như @nestjs/terminusopossum có thể được tích hợp để implement circuit breaker pattern trong NestJS applications.

Trace Propagation trong NestJS 11

NestJS 11 cải thiện đáng kể khả năng observability với built-in support cho OpenTelemetry trace propagation. Trace context được tự động truyền qua các microservice calls, giúp các nhà phát triển theo dõi request flow xuyên suốt hệ thống distributed. Điều này đặc biệt hữu ích khi debugging performance issues hoặc tracking down failures trong production.

Câu hỏi phỏng vấn NestJS Microservices

Sẵn sàng chinh phục phỏng vấn Node.js / NestJS?

Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.

Câu hỏi 1: Sự khác biệt giữa @MessagePattern và @EventPattern là gì?

Trả lời: @MessagePattern được sử dụng cho mô hình request-response đồng bộ, nơi caller gửi message và chờ đợi response. Decorator này phù hợp cho các operations cần xác nhận kết quả như tạo order hoặc lấy thông tin user. Ngược lại, @EventPattern dành cho mô hình event-driven bất đồng bộ, nơi publisher phát sự kiện mà không cần chờ phản hồi. Pattern này phù hợp cho notifications, logging hoặc triggering side effects không cần confirmation.

Câu hỏi 2: Giải thích cách NestJS xử lý gRPC server streaming

Trả lời: NestJS sử dụng RxJS Observables để implement gRPC server streaming. Khi một method được đánh dấu để return stream (trong file proto), controller method trả về Observable thay vì Promise. Mỗi value được emit từ Observable sẽ được gửi đến client như một message riêng biệt. Điều này cho phép server gửi dữ liệu incrementally mà không cần load toàn bộ dataset vào memory, phù hợp cho large result sets hoặc real-time updates.

Câu hỏi 3: Hybrid application trong NestJS hoạt động như thế nào?

Trả lời: Hybrid application cho phép một NestJS service chạy đồng thời nhiều transport layers. Sau khi tạo HTTP application với NestFactory.create(), các microservices được kết nối thông qua app.connectMicroservice(). Method startAllMicroservices() khởi động tất cả connected microservices, sau đó app.listen() bắt đầu HTTP server. Các controllers có thể xử lý cả HTTP requests và microservice messages, chia sẻ business logic thông qua services.

Câu hỏi 4: Tại sao exponential backoff với jitter quan trọng trong retry logic?

Trả lời: Exponential backoff tăng khoảng thời gian chờ giữa các lần retry theo cấp số nhân, giúp giảm tải cho downstream service đang gặp vấn đề. Jitter thêm một khoảng random vào delay để tránh thundering herd problem, nơi nhiều clients cùng retry đồng thời gây ra spike traffic. Kết hợp cả hai techniques giúp hệ thống recover smoothly hơn và tránh làm trầm trọng thêm tình trạng overload.

Câu hỏi 5: Circuit Breaker pattern giải quyết vấn đề gì trong microservices?

Trả lời: Circuit Breaker ngăn chặn cascade failures trong distributed systems. Khi một downstream service gặp sự cố, thay vì tiếp tục gửi requests và chờ timeout (tiêu tốn resources và tăng latency), circuit breaker "mở" sau một số failures nhất định. Trong trạng thái open, requests fail immediately với một fallback response hoặc error, cho phép downstream service thời gian để recover. Sau một khoảng thời gian, circuit chuyển sang half-open để test recovery trước khi close hoàn toàn.

Để củng cố kiến thức về NestJS microservices, các ứng viên nên thực hành thêm về NestJS modules và dependency injection cũng như middleware và interceptors.

Kết luận

Sẵn sàng chinh phục phỏng vấn Node.js / NestJS?

Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.

NestJS cung cấp một nền tảng vững chắc để xây dựng microservices architecture với nhiều transport options và patterns. Việc nắm vững các concepts này không chỉ giúp các nhà phát triển thiết kế hệ thống scalable mà còn chuẩn bị tốt cho các buổi phỏng vấn technical.

Các điểm chính cần ghi nhớ:

  • Transport Layer Architecture: Sử dụng @MessagePattern cho request-response và @EventPattern cho event-driven communication
  • gRPC Integration: Định nghĩa contracts với Protocol Buffers, cấu hình transport trong bootstrap và implement handlers với @GrpcMethod
  • Streaming Patterns: Tận dụng RxJS Observables để implement server streaming và bidirectional streaming
  • Hybrid Applications: Kết hợp HTTP và gRPC trong cùng một service để phục vụ cả frontend và inter-service communication
  • Domain-Driven Design: Xác định bounded contexts rõ ràng, sử dụng modules để tổ chức domain logic
  • Reliability Patterns: Implement timeouts, retries với exponential backoff và circuit breakers để xử lý failures gracefully
  • NestJS 11 Features: Tận dụng unwrap() method và built-in trace propagation cho better observability

Bắt đầu luyện tập!

Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.

Thẻ

#nestjs
#microservices
#grpc
#nodejs
#typescript

Chia sẻ

Bài viết liên quan