Files
blog.pyaqa.ru/AGENTS.md
Sergey Vanyushkin 87b094220d refactor: migrate to DDD architecture with Dishka DI
Complete architectural refactoring from simple MVC to Clean Architecture/DDD pattern:

Domain Layer:

- Add entities (Post, BaseEntity) with business logic

- Add value objects (Title, Content, Slug) with validation

- Add repository interfaces (PostRepository)

- Add domain exceptions

Application Layer:

- Add use cases (CreatePost, GetPost, UpdatePost, DeletePost, ListPosts, PublishPost)

- Add DTOs for data transfer

- Add TransactionManager interface

Infrastructure Layer:

- Add SQLAlchemy models and async database connection

- Add SQLAlchemyPostRepository implementation

- Add Dishka DI container with providers

- Add error handlers and middleware

Presentation Layer:

- Add FastAPI routes with Dishka integration

- Add Pydantic schemas

- Add dependency injection using FromDishka[T]

Other Changes:

- Remove old flat structure (api/, common/, core/, modules/)

- Add hatchling build system for package scripts

- Add blog CLI command

- Update AGENTS.md with new architecture docs

- All 48 tests passing, mypy clean, ruff clean
2026-05-01 20:20:41 +03:00

5.0 KiB

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

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 --fixruff formatisortmypy

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