Files
eryao/.trellis/spec/backend/directory-structure.md
T

145 lines
5.0 KiB
Markdown

# Directory Structure
> How backend code is organized in this project.
---
## Overview
This project follows a **layered architecture** with clear separation of concerns:
- **Schema → Repository → Service** layering pattern
- Router layer for HTTP endpoints
- Core infrastructure modules
- Domain schemas for business models
---
## Directory Layout
```
backend/
├── src/
│ ├── core/ # Infrastructure and cross-cutting concerns
│ │ ├── agentscope/ # Agent runtime framework
│ │ ├── auth/ # Authentication models and dependencies
│ │ ├── config/ # Settings and configuration
│ │ ├── db/ # Database base classes and session
│ │ ├── http/ # HTTP utilities (errors, middleware)
│ │ └── logging/ # Structured logging setup
│ ├── models/ # SQLAlchemy ORM models
│ ├── schemas/ # Business data models (Pydantic/dataclass)
│ │ ├── agent/ # Agent-related schemas
│ │ ├── domain/ # Domain objects
│ │ └── ...
│ ├── services/ # Shared infrastructure services
│ │ ├── base/ # Base service interfaces
│ │ ├── caches/ # Cache implementations
│ │ └── llm_pricing/ # LLM pricing service
│ └── v1/ # API v1 versioned modules
│ ├── agent/ # Agent module
│ ├── auth/ # Authentication module
│ ├── memories/ # Memories module
│ ├── points/ # Points module
│ └── ... # Other modules
├── tests/ # Test suites
├── alembic/ # Database migrations
│ └── versions/ # Migration files
└── pyproject.toml # Project configuration
```
---
## Module Organization
Each module under `v1/` follows consistent structure:
```
v1/<module>/
├── __init__.py # Module exports
├── router.py # FastAPI router (HTTP endpoints)
├── schemas.py # Request/response schemas (Pydantic)
├── repository.py # Data access layer (CRUD + queries)
├── service.py # Business logic layer (authz + transactions)
├── dependencies.py # FastAPI dependencies (DI)
└── utils.py # Module utilities (optional)
```
**Layering rules:**
- **Router** → handles HTTP, calls service, returns response
- **Service** → business logic, authz, transaction boundary, raises domain errors
- **Repository** → CRUD + query composition only, no auth decisions
**Example:** `v1/agent/` module:
- `router.py` defines `/agent` endpoints
- `service.py` enforces authz (`ensure_session_owner`) and coordinates repositories
- `repository.py` handles database queries for agent sessions
---
## Naming Conventions
### Files
- **snake_case**: Python files (e.g., `agent_service.py`)
- **Module directories**: Singular or plural based on domain (e.g., `agent/`, `memories/`)
### Classes
- **PascalCase**: Classes (`AgentService`, `AgentRepository`)
- **Suffixes**:
- `*Service` - Business logic layer
- `*Repository` - Data access layer
- `*Model` / `*Schema` - Data models (Pydantic)
- `*Settings` - Configuration classes
### Functions/Variables
- **snake_case**: Functions and variables (`get_by_id`, `soft_delete_by_id`)
- **Private prefix**: `_` for internal methods (`_apply_soft_delete_filter`)
### Database Tables
- **snake_case**: Table names (`agent_chat_session`, `llm_factory`)
- **Timestamps**: `created_at`, `updated_at`, `deleted_at` (soft delete)
- **Foreign keys**: `<entity>_id` (e.g., `factory_id`, `owner_id`)
---
## Examples
### Well-organized module: `v1/agent/`
```
v1/agent/
├── router.py # HTTP endpoints, dependencies injection
├── service.py # Business logic, authz checks (ensure_session_owner)
├── repository.py # Database queries
├── schemas.py # Request/response DTOs
├── dependencies.py # Service instantiation (DI)
├── utils.py # Helper functions
└── system_agents_config.py # Module config
```
### Core infrastructure: `core/logging/`
```
core/logging/
├── logger.py # get_logger() interface
├── config.py # Logging configuration
├── formatters.py # Structured log formatters
├── handlers.py # Log handlers
├── filters.py # Sensitive field filters
└── middleware.py # Request logging middleware
```
### ORM models: `models/`
```python
# models/agent_chat_session.py
class AgentChatSession(Base):
__tablename__ = "agent_chat_session"
id: Mapped[uuid.UUID] = ...
owner_id: Mapped[uuid.UUID] = ...
```
### Domain schemas: `schemas/domain/`
```python
# schemas/domain/chat_message.py
class AgentChatMessageMetadata(BaseModel):
role: str
content: str
# ... business fields
```