Как выстроить архитектуру FastAPI по принципам чистой архитектуры / DDD?
Подробный ответ
Главный принцип
Зависимости должны быть направлены внутрь: инфраструктура зависит от application и domain, а domain не зависит от FastAPI, SQLAlchemy, Redis или HTTP. Это уменьшает связанность и позволяет менять фреймворк, хранилище или транспорт без переписывания бизнес-правил.
Пример структуры
app/
domain/
orders/
entities.py
value_objects.py
repositories.py
services.py
application/
orders/
commands.py
use_cases.py
dto.py
infrastructure/
db/
models.py
repositories.py
messaging/
clients/
presentation/
api/
routers/
schemas/
dependencies.py
main.pyРоли слоёв
domain — сущности, value objects, инварианты и доменные события;
application — use cases, команды, транзакционные сценарии и порты;
infrastructure — SQLAlchemy repositories, кеш, брокер, внешние HTTP-клиенты;
presentation — FastAPI routers, Pydantic request/response schemas, аутентификация и HTTP-ошибки.
Use case и порты
Application layer зависит от абстракции репозитория, а инфраструктура реализует эту абстракцию. Router вызывает use case и преобразует HTTP-данные в command или DTO, не выполняя сам бизнес-логику.
from typing import Protocol
class OrderRepository(Protocol):
async def get(self, order_id: int) -> 'Order | None': ...
async def save(self, order: 'Order') -> None: ...
class CancelOrder:
def __init__(self, repository: OrderRepository):
self.repository = repository
async def execute(self, order_id: int) -> None:
order = await self.repository.get(order_id)
if order is None:
raise OrderNotFound(order_id)
order.cancel()
await self.repository.save(order)DDD на практике
DDD стоит применять там, где есть сложные правила и богатая предметная область: платежи, биллинг, логистика, права доступа. Для простого CRUD чрезмерное количество слоёв создаёт только boilerplate. Архитектура должна соответствовать сложности домена и команды.
Транзакции и события
Use case обычно определяет транзакционную границу. Интеграционные события стоит публиковать надёжно, например через outbox pattern, а не отправлять сообщение во внешний broker до успешного commit транзакции.
Как ответить на собеседовании
В чистой архитектуре FastAPI — это слой доставки HTTP, а не место для бизнес-логики. Routers вызывают application use cases, use cases работают с портами репозиториев, а SQLAlchemy и внешние сервисы остаются в infrastructure. DDD применяю к сложным bounded contexts и не навязываю тяжёлую архитектуру обычному CRUD.
Оцени свой прогресс