# 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**: ```prisma // 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: ```mermaid 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**: ```typescript // 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 => { 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 => { await prisma.$disconnect(); logger.info('Database disconnected / Đã ngắt kết nối database'); }; ``` ### Pattern Base Repository **Example from codebase / Ví dụ từ codebase**: ```typescript // 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 { 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 { 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 { 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 { 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 { 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(callback: (tx: any) => Promise): Promise { 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**: ```typescript // 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 { constructor(prisma: PrismaClient) { super(prisma, 'user'); } /** Find user by email * VI: Tìm người dùng theo email */ async findByEmail(email: string): Promise { 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 { 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 { 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**: ```typescript // services/iam-service/src/repositories/user-profile.repository.ts export class UserProfileRepository extends BaseRepository { /** 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 { 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**: ```typescript // services/iam-service/src/modules/feature/feature.repository.ts export class FeatureRepository extends BaseRepository { /** Find features by tags * VI: Tìm features theo tags */ async findByTags(tags: string[]): Promise { 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 `deletedAt` field when needed / Triển khai soft deletes với field `deletedAt` khi cần - ✅ Use `@default()` for default values / Sử dụng `@default()` cho giá trị mặc định - ✅ Use `@updatedAt` for automatic timestamp updates / Sử dụng `@updatedAt` cho cập nhật timestamp tự động ### Pattern Repository - ✅ Extend `BaseRepository` for common CRUD operations / Kế thừa `BaseRepository` cho CRUD operations chung - ✅ Add custom methods for domain-specific queries / Thêm methods tùy chỉnh cho queries đặc thù domain - ✅ Use `include` and `select` to control data fetching / Sử dụng `include` và `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: ```mermaid 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
throughout the flow ``` ### Tối Ưu Query - ✅ Use `select` to fetch only needed fields / Sử dụng `select` để chỉ lấy các fields cần thiết - ✅ Implement pagination with `skip` and `take` / Triển khai pagination với `skip` và `take` - ✅ Use indexes for frequently queried fields / Sử dụng indexes cho các fields thường query - ✅ Avoid N+1 queries by using `include` strategically / Tránh N+1 queries bằng cách sử dụng `include` chiến lược - ✅ Use `findUnique` instead of `findFirst` when possible / Sử dụng `findUnique` thay vì `findFirst` khi 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: ```mermaid 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`](../../../services/iam-service/prisma/schema.prisma) - **Auth Service Schema**: [`services/iam-service/prisma/schema.prisma`](../../../services/iam-service/prisma/schema.prisma) ### Ví Dụ Repository - **Base Repository**: [`services/iam-service/src/modules/common/repository.ts`](../../../services/iam-service/src/modules/common/repository.ts) - **User Repository**: [`services/iam-service/src/repositories/user.repository.ts`](../../../services/iam-service/src/repositories/user.repository.ts) - **User Profile Repository**: [`services/iam-service/src/repositories/user-profile.repository.ts`](../../../services/iam-service/src/repositories/user-profile.repository.ts) - **Feature Repository**: [`services/iam-service/src/modules/feature/feature.repository.ts`](../../../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`](../../../services/iam-service/src/config/database.config.ts) ## Tham Khảo Nhanh ### Các Thao Tác Prisma Thường Dùng ```typescript // 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](./api-design.md)**: For designing API endpoints that use repositories / Cho thiết kế API endpoints sử dụng repositories - **[Testing Patterns](./testing-patterns.md)**: For testing database operations / Cho test các thao tác database - **[Security](./security.md)**: 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](./observability-monitoring.md)**: 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](https://www.prisma.io/docs) - [Prisma Schema Reference](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference) - [Prisma Client API](https://www.prisma.io/docs/reference/api-reference/prisma-client-reference) - [PostgreSQL Documentation](https://www.postgresql.org/docs/) - [Neon Documentation](https://neon.tech/docs) ### Tài Liệu Nội Bộ - [Neon Database Guide](../guides/neon-database.md) - [Development Guide](../guides/development.md)