# Blog AGENTS.md ## Stack - Python 3.13+, FastAPI, pydantic, uvicorn - SQLAlchemy 2.0 (async), aiosqlite - Package manager: `uv` - CI: Woodpecker (lint, test, type on push/PR to `dev`) ## Commands ```bash uv sync --group dev # Install all dev dependencies uv run pytest # Run tests (coverage >= 70% required) uv run pytest tests/unit/ # Run single test directory uv run ruff check . --fix # Lint uv run ruff format # Format uv run isort . # Sort imports uv run mypy . # Type check (strict mode) uv run blog # Start dev server (port 8000) ``` ## Pre-commit order `ruff check --fix` → `ruff format` → `isort` → `mypy` ## DDD Architecture ### Layer Structure ``` app/ ├── domain/ # Domain Layer - business logic, no dependencies │ ├── entities/ # Domain entities (Post, User, etc.) │ │ ├── base.py # Base entity class │ │ └── post.py # Post entity with business logic │ ├── value_objects/ # Value objects (Title, Content, Slug) │ │ ├── base.py │ │ ├── title.py │ │ ├── content.py │ │ └── slug.py │ ├── repositories/ # Repository interfaces (abstract) │ │ ├── base.py │ │ └── post.py │ └── exceptions.py # Domain exceptions │ ├── application/ # Application Layer - use cases │ ├── dtos/ # Data Transfer Objects │ │ └── post.py │ ├── interfaces/ # Abstract interfaces (UoW) │ │ └── unit_of_work.py │ └── use_cases/ # Use cases (CQRS-like) │ ├── create_post.py │ ├── get_post.py │ ├── update_post.py │ ├── delete_post.py │ ├── list_posts.py │ └── publish_post.py │ ├── infrastructure/ # Infrastructure Layer - external concerns │ ├── config/ # Configuration │ │ └── settings.py │ ├── database/ # Database connection & ORM models │ │ ├── connection.py │ │ └── models.py │ ├── repositories/ # Repository implementations │ │ ├── post.py # SQLAlchemyPostRepository │ │ └── unit_of_work.py # SQLAlchemyUnitOfWork │ ├── di/ # Dependency Injection │ │ └── container.py │ └── middleware/ # Exception handlers │ └── error_handler.py │ ├── presentation/ # Presentation Layer - API │ ├── api/ # FastAPI routes │ │ ├── v1/ # API version 1 │ │ │ ├── __init__.py │ │ │ └── posts.py # Posts endpoints │ │ ├── deps.py # FastAPI dependencies │ │ └── __init__.py │ └── schemas/ # Pydantic schemas │ └── post.py │ └── main.py # Application entry point tests/ ├── unit/ # Unit tests (domain, use cases) │ ├── domain/ # Domain layer tests │ ├── application/ # Application layer tests │ └── infrastructure/ # Infrastructure tests ├── integration/ # Integration tests (DB, repos) ├── api/ # API endpoint tests └── e2e/ # End-to-end tests ``` ## Key Conventions ### Dependency Rule - Domain layer has **NO dependencies** on other layers - Application layer depends only on Domain - Infrastructure depends on Domain and Application - Presentation depends on all other layers ### Testing - **Unit tests**: Test domain logic without DB/external services - **Integration tests**: Test repository implementations with real DB - **API tests**: Test endpoints with mocked use cases - **E2E tests**: Full workflow testing ### Code Patterns - Use **dataclasses** for entities and value objects - Use **frozen dataclasses** for value objects (immutable) - Use **Unit of Work** pattern for transactions - Use **Repository** pattern for data access - Use **Dependency Injection** via FastAPI's Depends() ## DDD Concepts Used ### Entities - Have identity (UUID) - Mutable state - Business logic methods (publish, update_title, etc.) - Example: `Post` entity ### Value Objects - Immutable - Defined by attributes - Validated on creation - Examples: `Title`, `Content`, `Slug` ### Aggregates & Repositories - `Post` is an aggregate root - `PostRepository` interface in Domain - `SQLAlchemyPostRepository` implementation in Infrastructure ### Domain Events - Placeholder for future implementation - Can be added via event bus in application layer ## Configuration - `.env` file loaded by pydantic-settings - Settings available via `app.infrastructure.config.settings` ## Database - SQLAlchemy 2.0 with async support - SQLite by default (aiosqlite) - Tables auto-created on startup - Use `init_db()` and `close_db()` in lifespan