# Pattern Repository

Repository pattern implementation and best practices for GoodGo microservices including BaseRepository extension, custom queries, transactions, and query optimization.
> Implementation và best practices về repository pattern cho GoodGo microservices bao gồm mở rộng BaseRepository, custom queries, transactions, và query optimization.

## Tổng Quan

The Repository Pattern provides an abstraction layer between business logic and data access, making code more maintainable, testable, and consistent. This guide covers how to implement repositories using the BaseRepository class, write custom queries, handle transactions, and optimize database operations in GoodGo microservices.

Repository Pattern cung cấp abstraction layer giữa business logic và data access, làm cho code dễ bảo trì, testable, và nhất quán hơn. Hướng dẫn này bao gồm cách implement repositories sử dụng BaseRepository class, viết custom queries, xử lý transactions, và optimize database operations trong GoodGo microservices.

## Khi Nào Sử Dụng

Use the repository pattern when:
- Implementing data access layers for new modules
- Extending BaseRepository for specific entity types
- Writing custom database queries beyond standard CRUD
- Handling database transactions for multiple operations
- Optimizing database queries and operations
- Testing repository implementations
- Organizing data access code consistently

Sử dụng repository pattern khi:
- Implement data access layers cho modules mới
- Mở rộng BaseRepository cho các entity types cụ thể
- Viết custom database queries ngoài CRUD chuẩn
- Xử lý database transactions cho nhiều operations
- Optimize database queries và operations
- Testing repository implementations
- Tổ chức data access code một cách nhất quán

## Khái Niệm Chính

### Lợi Ích Của Repository Pattern

1. **Abstraction / Trừu Tượng Hóa**: Tách biệt business logic khỏi data access
2. **Testability / Khả Năng Test**: Dễ dàng mock repositories để test
3. **Maintainability / Khả Năng Bảo Trì**: Centralized database operations
4. **Consistency / Tính Nhất Quán**: Standardized CRUD operations
5. **Type Safety / An Toàn Kiểu**: TypeScript generics cung cấp type safety

### Kiến Trúc Repository

Repository pattern tạo abstraction layer giữa service layer và data access layer, cung cấp sự tách biệt rõ ràng giữa các concerns.

```mermaid
graph TB
    Controller["Controller<br/>(HTTP Handler)"] --> Service["Service Layer<br/>(Business Logic)"]
    Service --> Repository["Repository<br/>(Data Access)"]
    Repository --> Prisma["Prisma Client<br/>(ORM)"]
    Prisma --> Database[("Database<br/>(PostgreSQL)")]
    
    style Controller fill:#e1f5ff
    style Service fill:#fff4e1
    style Repository fill:#e8f5e9
    style Prisma fill:#f3e5f5
    style Database fill:#ffebee
```

### Class BaseRepository

The `BaseRepository` abstract class provides common database operations that can be extended by specific repositories. It handles error handling, logging, and provides type-safe methods for CRUD operations.

Class abstract `BaseRepository` cung cấp các database operations chung có thể được mở rộng bởi các repositories cụ thể. Nó xử lý error handling, logging, và cung cấp các methods type-safe cho CRUD operations.

**Available Methods / Các Methods Có Sẵn**:
- `findById(id)` - Find entity by ID / Tìm entity theo ID
- `findByUnique(field, value)` - Find by unique field / Tìm theo field duy nhất
- `findAll(options)` - Find all with filtering, pagination, sorting / Tìm tất cả với filtering, pagination, sorting
- `create(data)` - Create new entity / Tạo entity mới
- `update(id, data)` - Update entity / Cập nhật entity
- `delete(id)` - Delete entity / Xóa entity
- `count(where)` - Count entities / Đếm entities
- `exists(id)` - Check if entity exists / Kiểm tra entity có tồn tại
- `transaction(callback)` - Execute transaction / Thực thi transaction

### Cấu Trúc Class Hierarchy

Repositories extend abstract class `BaseRepository`, kế thừa các CRUD operations chung trong khi cho phép thêm custom query methods.

```mermaid
classDiagram
    class BaseRepository {
        <<abstract>>
        #prisma: PrismaClient
        #modelName: string
        +findById(id: string) Promise~T~null~
        +findByUnique(field: string, value: any) Promise~T~null~
        +findAll(options?: any) Promise~T[]~
        +create(data: CreateInput) Promise~T~
        +update(id: string, data: UpdateInput) Promise~T~
        +delete(id: string) Promise~boolean~
        +count(where?: any) Promise~number~
        +exists(id: string) Promise~boolean~
        +transaction(callback: Function) Promise~R~
    }
    
    class IRepository {
        <<interface>>
        +findById(id: string) Promise~T~null~
        +findByUnique(field: string, value: any) Promise~T~null~
        +findAll(options?: any) Promise~T[]~
        +create(data: CreateInput) Promise~T~
        +update(id: string, data: UpdateInput) Promise~T~
        +delete(id: string) Promise~boolean~
        +count(where?: any) Promise~number~
        +exists(id: string) Promise~boolean~
    }
    
    class UserRepository {
        +findByEmail(email: string) Promise~User~null~
        +findByUsername(username: string) Promise~User~null~
        +findWithPermissions(userId: string) Promise~User~null~
        +findActiveUsers(organizationId?: string) Promise~User[]~
    }
    
    class ProductRepository {
        +findByCategory(categoryId: string) Promise~Product[]~
        +findActiveProducts() Promise~Product[]~
    }
    
    BaseRepository <|-- UserRepository : extends
    BaseRepository <|-- ProductRepository : extends
    IRepository <|.. UserRepository : implements
```

**Lưu ý**: Các repositories cụ thể như `UserRepository` và `ProductRepository` extend `BaseRepository` và có thể implement interface `IRepository` để có thêm type safety. Chúng kế thừa tất cả base CRUD methods và thêm các domain-specific query methods.

**Tham Khảo**: [`services/iam-service/src/modules/common/repository.ts`](../../../services/iam-service/src/modules/common/repository.ts)

## Patterns

### Mở Rộng BaseRepository

Create a repository by extending BaseRepository and passing the Prisma model name.

Tạo repository bằng cách mở rộng BaseRepository và truyền tên Prisma model.

```typescript
import { PrismaClient, User } from '@prisma/client';
import { BaseRepository } from '../modules/common/repository';

export class UserRepository extends BaseRepository<User, CreateUserInput, UpdateUserInput> {
  constructor(prisma: PrismaClient) {
    super(prisma, 'user');
  }

// Thêm các methods tùy chỉnh
  async findByEmail(email: string): Promise<User | null> {
    return this.prisma.user.findUnique({
      where: { email },
    });
  }
}
```

**Tham Khảo**: [`services/iam-service/src/repositories/user.repository.ts`](../../../services/iam-service/src/repositories/user.repository.ts)

### Các Methods Query Tùy Chỉnh

Add domain-specific query methods for complex queries that go beyond standard CRUD operations.

Thêm các methods query domain-specific cho các queries phức tạp vượt quá CRUD operations chuẩn.

```typescript
export class UserRepository extends BaseRepository<User, CreateUserInput, UpdateUserInput> {
// Tìm user với dữ liệu liên quan
  async findWithPermissions(userId: string): Promise<User | null> {
    return this.prisma.user.findUnique({
      where: { id: userId },
      include: {
        userRoles: {
          include: { role: true },
        },
        userPermissions: {
          include: { permission: true },
        },
      },
    });
  }

// Query phức tạp với filtering
  async findActiveUsers(organizationId?: string): Promise<User[]> {
    return this.prisma.user.findMany({
      where: {
        isActive: true,
        ...(organizationId && { organizationId }),
      },
      orderBy: { createdAt: 'desc' },
    });
  }
}
```

### Sử Dụng Repository Interface

Implement the `IRepository` interface for additional type safety and consistency.

Implement interface `IRepository` để có thêm type safety và tính nhất quán.

```typescript
import { IRepository } from '../modules/common/repository';

export class UserRepository 
  extends BaseRepository<User, CreateUserInput, UpdateUserInput>
  implements IRepository<User, CreateUserInput, UpdateUserInput> {
  
  constructor(prisma: PrismaClient) {
    super(prisma, 'user');
  }
  
  // Implementation...
}
```

### Xử Lý Lỗi

BaseRepository handles errors automatically, wrapping database operations in try-catch blocks and converting errors to DatabaseError instances.

BaseRepository xử lý errors tự động, bọc các database operations trong try-catch blocks và chuyển đổi errors thành DatabaseError instances.

```typescript
// BaseRepository tự động xử lý errors
async findById(id: string): Promise<T | null> {
  try {
    const entity = await this.prisma.user.findUnique({ where: { id } });
    return entity;
  } catch (error: any) {
    logger.error(`Failed to find ${this.modelName} by ID`, { error, id });
    throw new DatabaseError(`Failed to find ${this.modelName}`, { id, originalError: error });
  }
}
```

### Transactions

Use the repository transaction method for operations that need to be atomic (all succeed or all fail).

Sử dụng method transaction của repository cho các operations cần atomic (tất cả thành công hoặc tất cả thất bại).

```typescript
// Thực thi nhiều operations trong transaction
await userRepository.transaction(async (tx) => {
  const user = await tx.user.create({ data: userData });
  await tx.userProfile.create({ 
    data: { userId: user.id, ...profileData } 
  });
  return user;
});
```

**Transaction Flow / Luồng Transaction**:

```mermaid
sequenceDiagram
    participant Service
    participant Repository
    participant Prisma as Prisma Client
    participant DB as Database
    
    Service->>Repository: transaction(callback)
    Repository->>Prisma: $transaction(callback)
    Prisma->>DB: BEGIN TRANSACTION
    
    Note over Service,DB: All operations use transaction client (tx)<br/>Tất cả operations sử dụng transaction client (tx)
    
    Service->>Repository: tx.user.create(data)
    Repository->>Prisma: tx.user.create(data)
    Prisma->>DB: INSERT INTO users ...
    DB-->>Prisma: User created
    Prisma-->>Repository: User entity
    Repository-->>Service: User entity
    
    Service->>Repository: tx.userProfile.create(data)
    Repository->>Prisma: tx.userProfile.create(data)
    Prisma->>DB: INSERT INTO user_profiles ...
    DB-->>Prisma: Profile created
    Prisma-->>Repository: Profile entity
    Repository-->>Service: Profile entity
    
    alt All operations succeed / Tất cả operations thành công
        Prisma->>DB: COMMIT
        DB-->>Prisma: Transaction committed
        Prisma-->>Repository: Success result
        Repository-->>Service: Success result
    else Error occurs / Lỗi xảy ra
        Prisma->>DB: ROLLBACK
        DB-->>Prisma: Transaction rolled back
        Prisma-->>Repository: Error thrown
        Repository-->>Service: DatabaseError thrown
    end
```

**Important / Quan Trọng**: Tất cả operations trong transaction callback phải sử dụng transaction client (`tx`) parameter, không phải main Prisma client, để đảm bảo tính atomic.

### Các Tùy Chọn Query

Use Prisma query options in findAll for filtering, pagination, sorting, and including relations.

Sử dụng các tùy chọn query của Prisma trong findAll để filtering, pagination, sorting, và include relations.

```typescript
// Phân trang
const users = await userRepository.findAll({
  skip: (page - 1) * limit,
  take: limit,
});

// Lọc
const activeUsers = await userRepository.findAll({
  where: { isActive: true },
});

// Sắp xếp
const recentUsers = await userRepository.findAll({
  orderBy: { createdAt: 'desc' },
});

// Bao gồm relations
const usersWithRoles = await userRepository.findAll({
  include: { userRoles: true },
});
```

## Thực Hành Tốt Nhất

1. **Extend BaseRepository / Mở Rộng BaseRepository**: Luôn extend BaseRepository thay vì implement từ đầu
2. **Custom Methods / Methods Tùy Chỉnh**: Thêm domain-specific query methods trong repository subclasses
3. **Type Safety / An Toàn Kiểu**: Sử dụng TypeScript generics cho type safety
4. **Error Handling / Xử Lý Lỗi**: Để BaseRepository xử lý common errors, xử lý domain-specific errors trong custom methods
5. **Logging / Ghi Log**: BaseRepository xử lý logging tự động
6. **Transactions / Transactions**: Sử dụng repository transaction method cho multi-step operations
7. **Query Optimization / Tối Ưu Query**: Sử dụng Prisma query options (select, include) để optimize queries
8. **Single Responsibility / Trách Nhiệm Đơn**: Mỗi repository xử lý một entity type
9. **Dependency Injection / Tiêm Phụ Thuộc**: Inject PrismaClient trong constructor để dễ test
10. **Avoid Business Logic / Tránh Business Logic**: Không đặt business logic trong repository, đặt trong service layer

## Lỗi Thường Gặp

1. **Not Extending BaseRepository / Không Mở Rộng BaseRepository**: Implement CRUD từ đầu thay vì extend
2. **Business Logic in Repository / Business Logic Trong Repository**: Đặt business logic trong repository thay vì service layer
3. **Exposing Prisma Client / Tiết Lộ Prisma Client**: Expose raw Prisma client thay vì sử dụng repository methods
4. **Missing Error Handling / Thiếu Xử Lý Lỗi**: Không xử lý errors trong custom query methods
5. **Over-fetching Data / Lấy Quá Nhiều Dữ Liệu**: Sử dụng `include` không cần thiết, fetch quá nhiều data
6. **No Type Safety / Không An Toàn Kiểu**: Không sử dụng TypeScript generics đúng cách
7. **Transaction Mistakes / Lỗi Transaction**: Không sử dụng repository transaction method cho related operations
8. **N+1 Query Problems / Vấn Đề N+1 Query**: Fetch related data trong loops thay vì sử dụng include
9. **Missing Indexes / Thiếu Indexes**: Không thêm database indexes cho các fields thường query

## Xử Lý Sự Cố

### Lỗi Kiểu Với Prisma

**Problem / Vấn Đề**: TypeScript errors khi sử dụng Prisma client methods

**Solution / Giải Pháp**: 
- Đảm bảo Prisma client đã được generate: `pnpm prisma generate`
- Sử dụng type assertions nếu cần: `(this.prisma as any)[this.modelName]`
- Verify Prisma schema matches TypeScript types

### Vấn Đề Rollback Transaction

**Problem / Vấn Đề**: Transaction không rollback khi có lỗi

**Solution / Giải Pháp**: 
- Đảm bảo tất cả operations trong transaction callback sử dụng transaction client (`tx`) parameter
- Không sử dụng main Prisma client trong transaction callback
- Let errors propagate, transaction will rollback automatically

**Example / Ví Dụ**:
```typescript
// ❌ Bad: Using main client in transaction
await repository.transaction(async (tx) => {
  await repository.prisma.user.create(...); // Wrong!
});

// ✅ Good: Using transaction client
await repository.transaction(async (tx) => {
  await tx.user.create(...); // Correct!
});
```

### Vấn Đề Hiệu Suất

**Problem / Vấn Đề**: Queries chậm hoặc N+1 query problems

**Solution / Giải Pháp**:
- Sử dụng `include` để fetch related data trong single query
- Sử dụng `select` để limit fields được fetch
- Thêm database indexes cho các fields thường query
- Avoid fetching unnecessary relations

**Example / Ví Dụ**:
```typescript
// ❌ Bad: N+1 query problem
const users = await userRepository.findAll();
for (const user of users) {
  const roles = await roleRepository.findByUserId(user.id); // N queries!
}

// ✅ Good: Single query with include
const users = await userRepository.findAll({
  include: { userRoles: { include: { role: true } } },
});
```

### Tên Model Không Khớp

**Problem / Vấn Đề**: Model name không khớp với Prisma schema

**Solution / Giải Pháp**:
- Verify model name matches Prisma schema exactly (case-sensitive)
- Use camelCase for model names (e.g., 'user', 'userProfile', 'accessRequest')
- Check Prisma schema for correct model names

## Tài Nguyên

- [BaseRepository](../../../services/iam-service/src/modules/common/repository.ts) - Base repository implementation
- [User Repository](../../../services/iam-service/src/repositories/user.repository.ts) - Example repository implementation
- [Database Prisma](./database-prisma.md) - Prisma ORM patterns và best practices
- [Error Handling](./error-handling-patterns.md) - Error handling patterns trong repositories
