> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tasteful.heka.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Repositories

> Understanding Repositories as the data access layer in Tasteful flavors

# Repositories

Repositories are the data access layer of Tasteful flavors. They provide an abstraction over data storage and retrieval, isolating business logic from database-specific operations. Repositories handle all interactions with databases, external APIs, and other data sources while providing a clean, testable interface for services.

## What is a Repository?

A Repository in Tasteful is a class that inherits from `BaseRepository` or `SQLModelRepository` and encapsulates data access logic. Repositories are responsible for:

* **Data Access Abstraction**: Hiding database-specific implementation details from business logic
* **CRUD Operations**: Providing Create, Read, Update, and Delete functionality
* **Query Management**: Handling complex queries and data filtering
* **Connection Management**: Managing database connections and sessions
* **Data Mapping**: Converting between database records and domain objects
* **Transaction Support**: Ensuring data consistency through proper transaction handling

Repositories act as the **single point of data access** for each entity or aggregate, providing a clean interface that services can use without knowing about database specifics.

## Core Concepts

### Repository Hierarchy

Tasteful provides two base repository classes:

#### BaseRepository

The foundational repository class that provides basic structure:

```python theme={null}
from tasteful.repositories import BaseRepository

class BaseRepository:
    def __init__(self, database_url: str = None, echo: bool = False):
        self.database_url = database_url
        self.echo = echo
        self._engine = None
```

#### SQLModelRepository

The recommended repository class for SQLModel-based applications:

```python theme={null}
from tasteful.repositories import SQLModelRepository

class UserRepository(SQLModelRepository):
    def __init__(self, database_url: str, echo: bool = False):
        super().__init__(database_url=database_url, echo=echo)
```

### Session Management

`SQLModelRepository` provides robust session management through context managers:

```python theme={null}
class UserRepository(SQLModelRepository):
    def get_user_by_id(self, user_id: int) -> Optional[User]:
        with self.get_session() as session:
            return session.get(User, user_id)
    
    def create_user(self, user_data: dict) -> User:
        with self.get_session() as session:
            user = User(**user_data)
            session.add(user)
            session.commit()
            session.refresh(user)
            return user
```

Key features of session management:

* **Automatic Rollback**: Sessions automatically rollback on exceptions
* **Connection Pooling**: Efficient database connection management
* **Transaction Safety**: Proper transaction boundaries for data consistency

## Implementation

### Basic Repository Implementation

Here's a complete example of a repository implementation:

```python theme={null}
from typing import List, Optional
from sqlmodel import select
from tasteful.repositories import SQLModelRepository

class UserRepository(SQLModelRepository):
    def __init__(self, database_url: str):
        super().__init__(database_url=database_url, echo=False)
    
    def get_by_id(self, user_id: int) -> Optional[User]:
        """Get a user by ID."""
        with self.get_session() as session:
            return session.get(User, user_id)
    
    def get_by_email(self, email: str) -> Optional[User]:
        """Get a user by email address."""
        with self.get_session() as session:
            statement = select(User).where(User.email == email)
            return session.exec(statement).first()
    
    def get_all(self, skip: int = 0, limit: int = 100) -> List[User]:
        """Get all users with pagination."""
        with self.get_session() as session:
            statement = select(User).offset(skip).limit(limit)
            return list(session.exec(statement).all())
    
    def create(self, user_data: dict) -> User:
        """Create a new user."""
        with self.get_session() as session:
            user = User(**user_data)
            session.add(user)
            session.commit()
            session.refresh(user)
            return user
    
    def update(self, user_id: int, user_data: dict) -> Optional[User]:
        """Update an existing user."""
        with self.get_session() as session:
            user = session.get(User, user_id)
            if not user:
                return None
            
            for key, value in user_data.items():
                setattr(user, key, value)
            
            session.add(user)
            session.commit()
            session.refresh(user)
            return user
    
    def delete(self, user_id: int) -> bool:
        """Delete a user by ID."""
        with self.get_session() as session:
            user = session.get(User, user_id)
            if not user:
                return False
            
            session.delete(user)
            session.commit()
            return True
```

### Advanced Query Patterns

Repositories can implement complex queries while maintaining clean interfaces:

```python theme={null}
class UserRepository(SQLModelRepository):
    def find_active_users_by_role(self, role: str) -> List[User]:
        """Find active users with a specific role."""
        with self.get_session() as session:
            statement = select(User).where(
                User.is_active == True,
                User.role == role
            )
            return list(session.exec(statement).all())
    
    def get_users_created_after(self, date: datetime) -> List[User]:
        """Get users created after a specific date."""
        with self.get_session() as session:
            statement = select(User).where(User.created_at > date)
            return list(session.exec(statement).all())
    
    def count_users_by_status(self, status: str) -> int:
        """Count users by status."""
        with self.get_session() as session:
            statement = select(User).where(User.status == status)
            return len(list(session.exec(statement).all()))
```

### Database Initialization

Repositories can handle database table creation:

```python theme={null}
class UserRepository(SQLModelRepository):
    def initialize_database(self):
        """Create database tables if they don't exist."""
        self.create_db_and_tables()
```

## Integration with Other Components

### Service Integration

Repositories are injected into services through dependency injection:

```python theme={null}
from tasteful.base_flavor import BaseService

class UserService(BaseService):
    def __init__(self, user_repository: UserRepository):
        super().__init__()
        self.user_repository = user_repository
    
    def create_user(self, user_data: dict) -> User:
        # Business logic validation
        if not user_data.get('email'):
            raise ValueError("Email is required")
        
        # Check if user already exists
        existing_user = self.user_repository.get_by_email(user_data['email'])
        if existing_user:
            raise ValueError("User with this email already exists")
        
        # Delegate to repository for data persistence
        return self.user_repository.create(user_data)
```

### Configuration Integration

Repositories receive configuration through dependency injection:

```python theme={null}
class UserRepository(SQLModelRepository):
    def __init__(self, app_config: AppConfig):
        super().__init__(
            database_url=app_config.database_url,
            echo=app_config.debug_sql
        )
        self.app_config = app_config
```

### Flavor Integration

Repositories are registered in flavor containers:

```python theme={null}
from tasteful.base_flavor import BaseFlavor

class UserFlavor(BaseFlavor):
    def __init__(self):
        super().__init__()
        
        # Register repository
        self.container.repository.add_singleton(
            UserRepository,
            app_config=self.container.config.app_config
        )
        
        # Register service with repository dependency
        self.container.service.add_singleton(
            UserService,
            user_repository=self.container.repository.user_repository
        )
```

## Best Practices

### Do's

* **Single Responsibility**: Each repository should handle one entity or aggregate
* **Interface Consistency**: Use consistent method naming across repositories (get\_by\_id, create, update, delete)
* **Session Management**: Always use the `get_session()` context manager for database operations
* **Error Handling**: Let database exceptions bubble up to services for proper business logic handling
* **Query Optimization**: Use appropriate indexes and query patterns for performance
* **Transaction Boundaries**: Keep transactions as short as possible while maintaining consistency

### Don'ts

* **Business Logic**: Don't put business rules in repositories - they belong in services
* **Direct Database Access**: Don't bypass the repository pattern by accessing the database directly from services
* **Session Leaks**: Don't store sessions as instance variables - always use context managers
* **Complex Joins**: Avoid overly complex queries that are hard to maintain - consider breaking them down
* **Tight Coupling**: Don't make repositories dependent on other repositories directly
