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 logic và data 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
choAuthService
. - 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ớiORM
(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ểm | Mô tả |
---|---|
Tách biệt rõ ràng | Controller, Service, Repository tách vai trò – dễ mở rộng và bảo trì. |
Tái sử dụng được | Repository có thể dùng lại ở nhiều service khác nhau. |
Dễ test | Mock repository dễ dàng khi unit test. |
Thay đổi ORM linh hoạt | Chỉ 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ỗi | Cách khắc phục |
---|---|
Gọi ORM trực tiếp trong Service | Tách riêng sang Repository. |
Không tạo interface cho repository | Luô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ừ database | Bọc lỗi trong try/catch và ném HttpException phù hợp. |
Tham khảo thêm
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.