Repository Pattern trong NestJS – Clean Architecture thực chiến

3 min read

Giới thiệu

Repository Pattern trong NestJS là một trong những cách phổ biến nhất để tổ chức tầng dữ liệu trong các ứng dụng backend hiện đại.
Nếu bạn đang phát triển một ứng dụng có nhiều module phức tạp, hoặc cần tách biệt rõ ràng giữa business logicdata access logic, thì Repository Pattern là lựa chọn bạn nên áp dụng ngay hôm nay.

Trong bài viết này, chúng ta sẽ cùng tìm hiểu:

  • Repository Pattern là gì và tại sao nên dùng trong NestJS.
  • Cách tổ chức thư mục và code theo chuẩn Clean Architecture.
  • Ví dụ thực tế: Xây dựng AuthRepository cho AuthService.
  • Những lỗi thường gặp và cách khắc phục..

Repository Pattern là gì?

Repository Pattern là một mô hình thiết kế giúp trừu tượng hóa (abstract) tầng truy cập dữ liệu (Data Access Layer) ra khỏi logic nghiệp vụ (Business Logic).

Nói đơn giản:

Thay vì để Service gọi trực tiếp tới ORM (như TypeORM, Prisma, hay thậm chí Airtable API), ta tạo một lớp “Repository” đóng vai trò trung gian.

Cấu trúc này giúp bạn:

  • Dễ thay đổi nguồn dữ liệu (chuyển từ PostgreSQL sang MongoDB mà không ảnh hưởng business logic).
  • Dễ test: có thể mock repository trong unit test.
  • Tuân thủ nguyên tắc Single Responsibility của Clean Architecture.

Cấu trúc thư mục Repository Pattern trong NestJS

Một cách tổ chức phổ biến (chuẩn Clean Architecture):

src/
 └── modules/
     └── auth/
         ├── controllers/
         │   └── auth.controller.ts
         ├── repositories/
         │   ├── auth.repository.ts
         │   └── auth.repository.interface.ts
         ├── services/
         │   └── auth.service.ts
         └── entities/
             └── user.entity.ts

Giải thích:

  • Controller: nhận request, trả response.
  • Service: xử lý logic nghiệp vụ.
  • Repository: làm việc với dữ liệu (database, API, v.v).
  • Entity: định nghĩa cấu trúc dữ liệu (User, Product,…).

Ví dụ thực tế: AuthModule với Repository Pattern

Giả sử bạn có module AuthModule dùng TypeORM.

Interface cho Repository

// src/modules/auth/repositories/auth.repository.interface.ts
import { User } from '../entities/user.entity';

export interface IAuthRepository {
  findByEmail(email: string): Promise<User | null>;
  createUser(userData: Partial<User>): Promise<User>;
}

Repository triển khai interface

// src/modules/auth/services/auth.service.ts
import { Injectable } from '@nestjs/common';
import { AuthRepository } from '../repositories/auth.repository';
import { User } from '../entities/user.entity';

@Injectable()
export class AuthService {
  constructor(private readonly authRepo: AuthRepository) {}

  async register(userData: Partial<User>) {
    const existing = await this.authRepo.findByEmail(userData.email);
    if (existing) throw new Error('Email already exists');
    return this.authRepo.createUser(userData);
  }
}

Service chỉ gọi repository, không gọi ORM trực tiếp

// src/modules/auth/services/auth.service.ts
import { Injectable } from '@nestjs/common';
import { AuthRepository } from '../repositories/auth.repository';
import { User } from '../entities/user.entity';

@Injectable()
export class AuthService {
  constructor(private readonly authRepo: AuthRepository) {}

  async register(userData: Partial<User>) {
    const existing = await this.authRepo.findByEmail(userData.email);
    if (existing) throw new Error('Email already exists');
    return this.authRepo.createUser(userData);
  }
}

Controller chỉ xử lý HTTP logic

// src/modules/auth/controllers/auth.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from '../services/auth.service';
import { User } from '../entities/user.entity';

@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @Post('register')
  async register(@Body() userData: Partial<User>) {
    return this.authService.register(userData);
  }
}

Ưu điểm của Repository Pattern trong NestJS

Ưu điểmMô tả
Tách biệt rõ ràngController, Service, Repository tách vai trò – dễ mở rộng và bảo trì.
Tái sử dụng đượcRepository có thể dùng lại ở nhiều service khác nhau.
Dễ testMock repository dễ dàng khi unit test.
Thay đổi ORM linh hoạtChỉ cần viết lại repository mà không ảnh hưởng logic nghiệp vụ.

Những lỗi thường gặp

LỗiCách khắc phục
Gọi ORM trực tiếp trong ServiceTách riêng sang Repository.
Không tạo interface cho repositoryLuôn định nghĩa interface để dễ mock/test.
Inject repository sai moduleĐảm bảo AuthRepository được đăng ký trong providers của AuthModule.
Không xử lý lỗi từ databaseBọc lỗi trong try/catch và ném HttpException phù hợp.

Tham khảo thêm

NestJS Official Documentation

Kết luận

Repository Pattern giúp bạn viết mã sạch (Clean Code), tuân thủ Clean Architecture, và dễ bảo trì trong dự án NestJS dài hạn.
Hãy bắt đầu refactor project của bạn theo mô hình này — chỉ sau vài ngày, bạn sẽ cảm nhận được sự khác biệt trong cách quản lý code, mở rộng module và viết test.

Avatar photo

Leave a Reply

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