Files
headquarter/openspec/changes/archive/2026-05-17-database-models/design.md
T

126 lines
2.9 KiB
Markdown

# Design: Database Models
## Technology Choices
- **SQLAlchemy 2.0**: Modern async ORM with type annotations
- **asyncpg**: High-performance async PostgreSQL driver
- **Alembic**: Database migration tool
- **UUID**: All primary keys use UUID for distributed safety
## Architecture
### Base Model
All models inherit from a common base with:
- `id`: UUID primary key (default=uuid4)
- `created_at`: Timestamp
- `updated_at`: Timestamp (auto-updated)
### Models
1. **User**
- id: UUID PK
- email: str, unique, indexed
- name: str
- authentik_id: str, unique (external auth reference)
- avatar_url: str | None
- created_at, updated_at
2. **Project**
- id: UUID PK
- name: str
- description: str | None
- owner_id: UUID → User
- default_ssh_key_id: UUID → SSHKey | None
- created_at, updated_at
3. **GitRepository**
- id: UUID PK
- name: str
- path: str (filesystem path to bare repo)
- project_id: UUID → Project
- owner_id: UUID → User
- is_mirror: bool
- remote_url: str | None
- last_push: datetime | None
- created_at
4. **SSHKey**
- id: UUID PK
- name: str
- public_key: str
- private_key_encrypted: str (Fernet encrypted)
- user_id: UUID → User
- project_id: UUID → Project | None
- created_at
5. **UserConfig**
- id: UUID PK
- user_id: UUID → User
- config: JSONB (PostgreSQL native JSON)
- created_at, updated_at
### Relationships
```
User 1--N Project
User 1--N SSHKey
User 1--1 UserConfig
Project 1--N GitRepository
Project N--1 SSHKey (default_ssh_key)
```
### File Structure
```
apps/api/
├── src/
│ ├── models/
│ │ ├── __init__.py
│ │ ├── base.py # DeclarativeBase + common columns
│ │ ├── user.py
│ │ ├── project.py
│ │ ├── git_repository.py
│ │ ├── ssh_key.py
│ │ └── user_config.py
│ ├── database.py # Async engine + session
│ └── config.py # Settings with pydantic-settings
├── alembic/
│ ├── env.py
│ ├── script.py.mako
│ └── versions/
│ └── 001_initial.py
├── tests/
│ └── test_models.py
└── scripts/
└── seed.py
```
## Async Pattern
```python
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker
engine = create_async_engine(DATABASE_URL)
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession)
```
## Migration Strategy
- Single initial migration creating all tables
- Future migrations use `alembic revision --autogenerate`
- Run with `make migrate` (docker compose exec api alembic upgrade head)
## Seed Data
- Create a test user with sample data
- Run via `docker compose exec api python scripts/seed.py`
## Quality Gates
- pytest with async test support
- mypy strict mode
- ruff for linting
- All models have type annotations