- Renamed auth-service to iam-service across various files for consistency. - Updated Dockerfiles, deployment configurations, and documentation to reflect the service name change. - Enhanced testing commands in documentation to point to the new iam-service. - Removed outdated auth-service files and configurations to streamline the project structure. - Improved bilingual documentation for clarity on the new service structure and usage.
21 KiB
Database & Prisma / Cơ Sở Dữ Liệu & Prisma
EN: Prisma ORM and database patterns for GoodGo microservices. Use when working with databases, creating Prisma schemas, writing migrations, implementing repositories, or optimizing queries. VI: Pattern Prisma ORM và database cho các microservices của GoodGo. Sử dụng khi làm việc với databases, tạo Prisma schemas, viết migrations, triển khai repositories, hoặc tối ưu queries.
Overview / Tổng Quan
EN: This skill covers Prisma ORM patterns, database schema design, repository patterns, query optimization, and transaction handling used across GoodGo microservices. It ensures type-safe database operations, consistent data access patterns, and optimal performance.
VI: Skill này bao gồm các pattern Prisma ORM, thiết kế database schema, repository patterns, tối ưu queries, và xử lý transactions được sử dụng trong các microservices của GoodGo. Nó đảm bảo các thao tác database type-safe, pattern truy cập dữ liệu nhất quán, và hiệu năng tối ưu.
When to Use / Khi Nào Sử Dụng
EN: Use this skill when:
- Setting up Prisma for a new service
- Creating or modifying database schemas
- Writing database migrations
- Implementing repository patterns
- Optimizing database queries
- Setting up database connections
- Implementing transactions
- Working with Neon PostgreSQL
VI: Sử dụng skill này khi:
- Thiết lập Prisma cho service mới
- Tạo hoặc sửa đổi database schemas
- Viết database migrations
- Triển khai repository patterns
- Tối ưu database queries
- Thiết lập kết nối database
- Triển khai transactions
- Làm việc với Neon PostgreSQL
Key Concepts / Khái Niệm Chính
1. Repository Pattern / Pattern Repository
EN: All database operations go through repository classes that extend BaseRepository, providing consistent CRUD operations and error handling.
VI: Tất cả các thao tác database đi qua các repository classes kế thừa BaseRepository, cung cấp các CRUD operations nhất quán và xử lý lỗi.
2. Prisma Schema / Schema Prisma
EN: Database schema is defined in prisma/schema.prisma with models, relations, indexes, and constraints.
VI: Database schema được định nghĩa trong prisma/schema.prisma với models, relations, indexes, và constraints.
3. Type Safety / An Toàn Kiểu
EN: Prisma generates TypeScript types from schema, ensuring type-safe database operations.
VI: Prisma generate TypeScript types từ schema, đảm bảo các thao tác database type-safe.
Common Patterns / Các Pattern Thường Dùng
Prisma Schema Pattern / Pattern Schema Prisma
Example from codebase / Ví dụ từ codebase:
// services/iam-service/prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// EN: User model - Core user entity
// VI: Model User - Entity người dùng cốt lõi
model User {
id String @id @default(cuid())
email String @unique
username String? @unique
passwordHash String? // Nullable for social-only users
isActive Boolean @default(true)
emailVerified Boolean @default(false)
mfaEnabled Boolean @default(false)
mfaSecret String?
lastLoginAt DateTime?
loginCount Int @default(0)
organizationId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
userRoles UserRole[]
userPermissions UserPermission[]
sessions Session[]
organization Organization? @relation(fields: [organizationId], references: [id])
profile UserProfile?
@@index([email])
@@index([username])
@@index([createdAt])
@@index([organizationId])
@@map("users")
}
Database Connection Pattern / Pattern Kết Nối Database
Example from codebase / Ví dụ từ codebase:
// services/iam-service/src/config/database.config.ts
import { PrismaClient } from '@prisma/client';
import { logger } from '@goodgo/logger';
/**
* EN: Prisma client instance configured for the application
* VI: Instance Prisma client được cấu hình cho ứng dụng
*/
export const prisma = new PrismaClient({
// EN: Enable detailed logging in development, minimal in production
// VI: Bật ghi log chi tiết trong development, tối thiểu trong production
log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
});
/**
* EN: Establish database connection on application startup
* VI: Thiết lập kết nối database khi khởi động ứng dụng
*/
export const connectDatabase = async (): Promise<void> => {
try {
await prisma.$connect();
logger.info('Database connected successfully / Kết nối database thành công');
} catch (error) {
logger.error('Database connection failed / Kết nối database thất bại', { error });
process.exit(1);
}
};
/**
* EN: Close database connection on application shutdown
* VI: Đóng kết nối database khi tắt ứng dụng
*/
export const disconnectDatabase = async (): Promise<void> => {
await prisma.$disconnect();
logger.info('Database disconnected / Đã ngắt kết nối database');
};
Base Repository Pattern / Pattern Base Repository
Example from codebase / Ví dụ từ codebase:
// services/iam-service/src/modules/common/repository.ts
import { PrismaClient } from '@prisma/client';
import { logger } from '@goodgo/logger';
import { DatabaseError } from '../../errors/http-error';
/**
* EN: Base repository class providing common database operations
* VI: Base repository class cung cấp các thao tác database chung
*/
export abstract class BaseRepository<T, CreateInput, UpdateInput> {
protected prisma: PrismaClient;
protected modelName: string;
constructor(prisma: PrismaClient, modelName: string) {
this.prisma = prisma;
this.modelName = modelName;
}
/**
* EN: Find entity by ID
* VI: Tìm entity theo ID
*/
async findById(id: string): Promise<T | null> {
try {
logger.debug(`Finding ${this.modelName} by ID / Tìm ${this.modelName} theo ID`, { id });
const entity = await (this.prisma as any)[this.modelName].findUnique({
where: { id },
});
logger.debug(`${this.modelName} ${entity ? 'found' : 'not found'} / ${this.modelName} ${entity ? 'đã tìm thấy' : 'không tìm thấy'}`, { id });
return entity;
} catch (error: any) {
logger.error(`Failed to find ${this.modelName} by ID / Không thể tìm ${this.modelName} theo ID`, { error, id });
throw new DatabaseError(`Failed to find ${this.modelName}`, { id, originalError: error });
}
}
/**
* EN: Find all entities with optional filtering
* VI: Tìm tất cả entities với filtering tùy chọn
*/
async findAll(options?: {
where?: any;
orderBy?: any;
skip?: number;
take?: number;
include?: any;
}): Promise<T[]> {
try {
logger.debug(`Finding all ${this.modelName} / Tìm tất cả ${this.modelName}`, options);
const entities = await (this.prisma as any)[this.modelName].findMany(options || {});
logger.debug(`Found ${entities.length} ${this.modelName} entities / Đã tìm thấy ${entities.length} ${this.modelName} entities`);
return entities;
} catch (error: any) {
logger.error(`Failed to find all ${this.modelName} / Không thể tìm tất cả ${this.modelName}`, { error, options });
throw new DatabaseError(`Failed to find ${this.modelName} entities`, { options, originalError: error });
}
}
/**
* EN: Create new entity
* VI: Tạo entity mới
*/
async create(data: CreateInput): Promise<T> {
try {
logger.debug(`Creating new ${this.modelName} / Tạo ${this.modelName} mới`, { data });
const entity = await (this.prisma as any)[this.modelName].create({
data,
});
logger.debug(`${this.modelName} created successfully / ${this.modelName} đã được tạo thành công`, { id: (entity as any).id });
return entity;
} catch (error: any) {
logger.error(`Failed to create ${this.modelName} / Không thể tạo ${this.modelName}`, { error, data });
throw new DatabaseError(`Failed to create ${this.modelName}`, { data, originalError: error });
}
}
/**
* EN: Update entity by ID
* VI: Cập nhật entity theo ID
*/
async update(id: string, data: UpdateInput): Promise<T> {
try {
logger.debug(`Updating ${this.modelName} / Cập nhật ${this.modelName}`, { id, data });
const entity = await (this.prisma as any)[this.modelName].update({
where: { id },
data,
});
logger.debug(`${this.modelName} updated successfully / ${this.modelName} đã được cập nhật thành công`, { id });
return entity;
} catch (error: any) {
if (error.code === 'P2025') {
logger.warn(`${this.modelName} not found for update / ${this.modelName} không tìm thấy để cập nhật`, { id });
throw new DatabaseError(`${this.modelName} not found`, { id });
}
logger.error(`Failed to update ${this.modelName} / Không thể cập nhật ${this.modelName}`, { error, id, data });
throw new DatabaseError(`Failed to update ${this.modelName}`, { id, data, originalError: error });
}
}
/**
* EN: Execute transaction with multiple operations
* VI: Thực thi transaction với nhiều operations
*/
async transaction<R>(callback: (tx: any) => Promise<R>): Promise<R> {
try {
logger.debug(`Starting ${this.modelName} transaction / Bắt đầu transaction ${this.modelName}`);
const result = await this.prisma.$transaction(async (tx) => {
return await callback(tx);
});
logger.debug(`${this.modelName} transaction completed successfully / Transaction ${this.modelName} đã hoàn thành thành công`);
return result;
} catch (error: any) {
logger.error(`${this.modelName} transaction failed / Transaction ${this.modelName} thất bại`, { error });
throw new DatabaseError(`${this.modelName} transaction failed`, { originalError: error });
}
}
}
Specific Repository Implementation / Triển Khai Repository Cụ Thể
Example from codebase / Ví dụ từ codebase:
// services/iam-service/src/repositories/user.repository.ts
import { PrismaClient, User } from '@prisma/client';
import { BaseRepository } from '../modules/common/repository';
/**
* EN: User repository for database operations
* VI: Repository người dùng cho các thao tác database
*/
export class UserRepository extends BaseRepository<User, any, any> {
constructor(prisma: PrismaClient) {
super(prisma, 'user');
}
/**
* EN: Find user by email
* VI: Tìm người dùng theo email
*/
async findByEmail(email: string): Promise<User | null> {
return this.prisma.user.findUnique({
where: { email },
include: {
userRoles: {
include: { role: true },
},
userPermissions: {
include: { permission: true },
},
},
});
}
/**
* EN: Find user by username
* VI: Tìm người dùng theo username
*/
async findByUsername(username: string): Promise<User | null> {
return this.prisma.user.findUnique({
where: { username },
});
}
/**
* EN: Find user with roles and permissions
* VI: Tìm người dùng với roles và permissions
*/
async findWithPermissions(userId: string): Promise<User | null> {
return this.prisma.user.findUnique({
where: { id: userId },
include: {
userRoles: {
where: {
OR: [
{ expiresAt: null },
{ expiresAt: { gte: new Date() } },
],
},
include: {
role: {
include: {
permissions: {
include: { permission: true },
},
},
},
},
},
userPermissions: {
where: {
OR: [
{ expiresAt: null },
{ expiresAt: { gte: new Date() } },
],
},
include: { permission: true },
},
},
});
}
}
Upsert Pattern / Pattern Upsert
Example from codebase / Ví dụ từ codebase:
// services/iam-service/src/repositories/user-profile.repository.ts
export class UserProfileRepository extends BaseRepository<UserProfile, any, any> {
/**
* EN: Create or update profile for user
* VI: Tạo hoặc cập nhật profile cho user
*/
async upsert(userId: string, data: any): Promise<UserProfile> {
return this.prisma.userProfile.upsert({
where: { userId },
update: {
...data,
updatedAt: new Date(),
},
create: {
userId,
...data,
},
include: {
user: {
select: {
id: true,
email: true,
username: true,
},
},
},
});
}
}
Query Optimization Patterns / Pattern Tối Ưu Query
Example from codebase / Ví dụ từ codebase:
// services/iam-service/src/modules/feature/feature.repository.ts
export class FeatureRepository extends BaseRepository<Feature, CreateFeatureInput, UpdateFeatureInput> {
/**
* EN: Find features by tags
* VI: Tìm features theo tags
*/
async findByTags(tags: string[]): Promise<Feature[]> {
try {
logger.debug('Finding features by tags / Tìm features theo tags', { tags });
const features = await this.prisma.feature.findMany({
where: {
tags: {
hasSome: tags,
},
},
orderBy: { createdAt: 'desc' },
});
logger.debug(`Found ${features.length} features by tags / Đã tìm thấy ${features.length} features theo tags`, { tags });
return features;
} catch (error) {
logger.error('Failed to find features by tags / Không thể tìm features theo tags', { error, tags });
throw this.handleDatabaseError(error, { tags });
}
}
}
Best Practices / Thực Hành Tốt Nhất
1. Schema Design / Thiết Kế Schema
- ✅ Use appropriate field types (String, Int, Boolean, DateTime, Json) / Sử dụng field types phù hợp
- ✅ Add indexes for frequently queried fields (
@@index([email])) / Thêm indexes cho các fields thường query - ✅ Use relations instead of storing JSON when possible / Sử dụng relations thay vì lưu JSON khi có thể
- ✅ Implement soft deletes with
deletedAtfield when needed / Triển khai soft deletes với fielddeletedAtkhi cần - ✅ Use
@default()for default values / Sử dụng@default()cho giá trị mặc định - ✅ Use
@updatedAtfor automatic timestamp updates / Sử dụng@updatedAtcho cập nhật timestamp tự động
2. Repository Pattern / Pattern Repository
- ✅ Extend
BaseRepositoryfor common CRUD operations / Kế thừaBaseRepositorycho CRUD operations chung - ✅ Add custom methods for domain-specific queries / Thêm methods tùy chỉnh cho queries đặc thù domain
- ✅ Use
includeandselectto control data fetching / Sử dụngincludevàselectđể kiểm soát việc lấy dữ liệu - ✅ Handle Prisma errors and convert to domain errors / Xử lý lỗi Prisma và chuyển đổi sang domain errors
- ✅ Log database operations for debugging / Log các thao tác database để debug
3. Query Optimization / Tối Ưu Query
- ✅ Use
selectto fetch only needed fields / Sử dụngselectđể chỉ lấy các fields cần thiết - ✅ Implement pagination with
skipandtake/ Triển khai pagination vớiskipvàtake - ✅ Use indexes for frequently queried fields / Sử dụng indexes cho các fields thường query
- ✅ Avoid N+1 queries by using
includestrategically / Tránh N+1 queries bằng cách sử dụngincludechiến lược - ✅ Use
findUniqueinstead offindFirstwhen possible / Sử dụngfindUniquethay vìfindFirstkhi có thể - ✅ Use transactions for multiple related operations / Sử dụng transactions cho nhiều operations liên quan
4. Error Handling / Xử Lý Lỗi
- ✅ Catch Prisma errors and convert to domain errors / Bắt lỗi Prisma và chuyển đổi sang domain errors
- ✅ Handle unique constraint violations (P2002) / Xử lý vi phạm ràng buộc duy nhất
- ✅ Handle record not found (P2025) / Xử lý không tìm thấy bản ghi
- ✅ Log errors with context / Log lỗi kèm context
- ✅ Provide meaningful error messages / Cung cấp thông báo lỗi có ý nghĩa
5. Migrations / Migrations
- ✅ Keep migrations small and focused / Giữ migrations nhỏ và tập trung
- ✅ Test migrations before production / Test migrations trước khi production
- ✅ Backup before major changes / Backup trước khi thay đổi lớn
- ✅ Use descriptive migration names / Sử dụng tên migration mô tả
- ✅ Review generated SQL before applying / Xem xét SQL được generate trước khi áp dụng
6. Connection Management / Quản Lý Kết Nối
- ✅ Use connection pooling for production / Sử dụng connection pooling cho production
- ✅ Close connections on application shutdown / Đóng kết nối khi tắt ứng dụng
- ✅ Handle connection errors gracefully / Xử lý lỗi kết nối một cách lịch sự
- ✅ Monitor connection pool usage / Giám sát việc sử dụng connection pool
Examples from Project / Ví Dụ Từ Dự Án
Schema Examples / Ví Dụ Schema
- IAM Service Schema:
services/iam-service/prisma/schema.prisma - Auth Service Schema:
services/iam-service/prisma/schema.prisma
Repository Examples / Ví Dụ Repository
- Base Repository:
services/iam-service/src/modules/common/repository.ts - User Repository:
services/iam-service/src/repositories/user.repository.ts - User Profile Repository:
services/iam-service/src/repositories/user-profile.repository.ts - Feature Repository:
services/iam-service/src/modules/feature/feature.repository.ts
Database Config Examples / Ví Dụ Cấu Hình Database
- Database Config:
services/iam-service/src/config/database.config.ts
Quick Reference / Tham Khảo Nhanh
Common Prisma Operations / Các Thao Tác Prisma Thường Dùng
// Find by ID / Tìm theo ID
const user = await prisma.user.findUnique({ where: { id } });
// Find with relations / Tìm với relations
const user = await prisma.user.findUnique({
where: { id },
include: { profile: true, roles: true },
});
// Find many with filters / Tìm nhiều với filters
const users = await prisma.user.findMany({
where: { isActive: true },
skip: 0,
take: 10,
orderBy: { createdAt: 'desc' },
});
// Create / Tạo
const user = await prisma.user.create({
data: { email, passwordHash },
});
// Update / Cập nhật
const user = await prisma.user.update({
where: { id },
data: { isActive: false },
});
// Delete / Xóa
await prisma.user.delete({ where: { id } });
// Upsert / Tạo hoặc cập nhật
const profile = await prisma.userProfile.upsert({
where: { userId },
update: { ...data },
create: { userId, ...data },
});
// Transaction / Transaction
await prisma.$transaction(async (tx) => {
const user = await tx.user.create({ data: userData });
await tx.userProfile.create({ data: { userId: user.id, ...profileData } });
});
Prisma Error Codes / Mã Lỗi Prisma
| Code | Description / Mô Tả |
|---|---|
| P2002 | Unique constraint violation / Vi phạm ràng buộc duy nhất |
| P2025 | Record not found / Không tìm thấy bản ghi |
| P2003 | Foreign key constraint violation / Vi phạm ràng buộc khóa ngoại |
| P2014 | Required relation violation / Vi phạm quan hệ bắt buộc |
Related Skills / Skills Liên Quan
- API Design: For designing API endpoints that use repositories / Cho thiết kế API endpoints sử dụng repositories
- Testing Patterns: For testing database operations / Cho test các thao tác database
- Security: For securing database operations and preventing SQL injection / Cho bảo mật các thao tác database và ngăn chặn SQL injection
- Observability: For monitoring database performance / Cho giám sát hiệu năng database
Resources / Tài Nguyên
External Documentation / Tài Liệu Bên Ngoài
- Prisma Documentation
- Prisma Schema Reference
- Prisma Client API
- PostgreSQL Documentation
- Neon Documentation