- Added request/response flow diagrams to api-design and api-gateway-advanced skills for better visualization of processes. - Introduced configuration loading flow in configuration-management skill to clarify the configuration process. - Included error propagation flow in error-handling-patterns skill to illustrate error handling across layers. - Enhanced various skills with additional diagrams to improve understanding of complex concepts. These updates aim to provide clearer guidance and improve the overall documentation experience for developers.
23 KiB
Cơ Sở Dữ Liệu & Prisma
Prisma ORM and database patterns for GoodGo microservices. Use when working with databases, creating Prisma schemas, writing migrations, implementing repositories, or optimizing queries.
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.
Tổng Quan
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.
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.
Khi Nào Sử Dụng
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
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
Khái Niệm Chính
Pattern Repository
All database operations go through repository classes that extend BaseRepository, providing consistent CRUD operations and error handling.
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.
Schema Prisma
Database schema is defined in prisma/schema.prisma with models, relations, indexes, and constraints.
Database schema được định nghĩa trong prisma/schema.prisma với models, relations, indexes, và constraints.
An Toàn Kiểu
Prisma generates TypeScript types from schema, ensuring type-safe database operations.
Prisma generate TypeScript types từ schema, đảm bảo các thao tác database type-safe.
Các Pattern Thường Dùng
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")
}
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")
}
Sơ Đồ Quan Hệ Schema / Schema Relationships Diagram
Sơ đồ sau minh họa các quan hệ giữa các models User, Post, và Profile:
The following diagram illustrates the relationships between User, Post, and Profile models:
erDiagram
User ||--o{ Post : "has many"
User ||--o| Profile : "has one"
User {
string id PK
string email UK
string name
string password
enum role
datetime createdAt
datetime updatedAt
}
Post {
string id PK
string title
string content
boolean published
string authorId FK
datetime createdAt
datetime updatedAt
}
Profile {
string id PK
string bio
string avatar
string userId FK,UK
}
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';
/**
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({
Enable detailed logging in development, minimal in production
// VI: Bật ghi log chi tiết trong development, tối thiểu trong production
['error'],
});
/**
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);
}
};
/**
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');
};
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';
/**
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;
}
/**
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 });
}
}
/**
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 });
}
}
/**
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 });
}
}
/**
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 });
}
}
/**
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 });
}
}
}
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';
/**
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');
}
/**
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 },
},
},
});
}
/**
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 },
});
}
/**
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 },
},
},
});
}
}
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> {
/**
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,
},
},
},
});
}
}
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> {
/**
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 });
}
}
}
Thực Hành Tốt Nhất
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
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
Luồng Thực Thi Query / Query Execution Flow
Sơ đồ tuần tự sau minh họa cách các Prisma queries chảy từ lớp ứng dụng đến database:
The following sequence diagram illustrates how Prisma queries flow from the application layer to the database:
sequenceDiagram
participant App as Application
participant Repo as Repository
participant Client as Prisma Client
participant Pool as Connection Pool
participant DB as PostgreSQL
App->>Repo: findById(id)
Repo->>Client: prisma.user.findUnique()
Client->>Client: Validate query
Client->>Client: Generate SQL
Client->>Pool: Request connection
Pool->>DB: Execute SQL query
DB-->>Pool: Return results
Pool-->>Client: Return data
Client->>Client: Transform to TypeScript types
Client-->>Repo: Return typed result
Repo-->>App: Return User | null
Note over App,DB: Prisma ensures type safety<br/>throughout the flow
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
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
Quy Trình Migration / Migration Workflow
Sơ đồ sau mô tả quy trình migration điển hình từ development đến production:
The following diagram shows the typical migration workflow from development to production:
flowchart TD
Start([Start Migration]) --> EditSchema[Edit schema.prisma]
EditSchema --> CreateMigration[Run: prisma migrate dev]
CreateMigration --> GenerateSQL[Prisma generates SQL]
GenerateSQL --> ReviewSQL{Review SQL?}
ReviewSQL -->|Yes| CheckSQL[Check migration SQL]
ReviewSQL -->|No| ApplyDev[Apply to dev database]
CheckSQL --> ApplyDev
ApplyDev --> GenerateClient[Generate Prisma Client]
GenerateClient --> TestDev[Test in development]
TestDev --> TestPass{Tests pass?}
TestPass -->|No| FixIssues[Fix issues]
FixIssues --> EditSchema
TestPass -->|Yes| CommitMigration[Commit migration files]
CommitMigration --> DeployProd[Deploy to production]
DeployProd --> RunDeploy[Run: prisma migrate deploy]
RunDeploy --> End([Migration Complete])
style Start fill:#e1f5e1
style End fill:#e1f5e1
style TestPass fill:#fff4e1
style ReviewSQL fill:#fff4e1
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
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
Ví Dụ Từ Dự Án
Ví Dụ Schema
- IAM Service Schema:
services/iam-service/prisma/schema.prisma - Auth Service Schema:
services/iam-service/prisma/schema.prisma
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
Ví Dụ Cấu Hình Database
- Database Config:
services/iam-service/src/config/database.config.ts
Tham Khảo Nhanh
Các Thao Tác Prisma Thường Dùng
// Tìm theo ID
const user = await prisma.user.findUnique({ where: { id } });
// Tìm với relations
const user = await prisma.user.findUnique({
where: { id },
include: { profile: true, roles: true },
});
// Tìm nhiều với filters
const users = await prisma.user.findMany({
where: { isActive: true },
skip: 0,
take: 10,
orderBy: { createdAt: 'desc' },
});
// Tạo
const user = await prisma.user.create({
data: { email, passwordHash },
});
// Cập nhật
const user = await prisma.user.update({
where: { id },
data: { isActive: false },
});
// Xóa
await prisma.user.delete({ where: { id } });
// Tạo hoặc cập nhật
const profile = await prisma.userProfile.upsert({
where: { userId },
update: { ...data },
create: { userId, ...data },
});
// Transaction
await prisma.$transaction(async (tx) => {
const user = await tx.user.create({ data: userData });
await tx.userProfile.create({ data: { userId: user.id, ...profileData } });
});
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 |
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
Tài Nguyên
Tài Liệu Bên Ngoài
- Prisma Documentation
- Prisma Schema Reference
- Prisma Client API
- PostgreSQL Documentation
- Neon Documentation