Программирование с ИИ
ENRU
7.4

Читаем бэкенд-код

роуты, модели, сервисы и кто кого вызывает

Что это

Бэкенд-проект почти всегда разложен по слоям, и слои эти одинаковые везде: маршруты, схемы, бизнес-логика, работа с базой. Разные фреймворки называют их по-разному, но делят одинаково.

Зная деление, в незнакомом проекте находишь нужное за минуту вместо получасового блуждания.

Как выглядит в коде

измерено
app/
├── main.py              создание приложения, подключение роутеров
├── routers/             маршруты: что на каком адресе
│   ├── subs.py
│   └── users.py
├── schemas/             схемы запросов и ответов (статья 7.3)
│   └── subs.py
├── services/            бизнес-логика: правила предметной области
│   └── subs.py
├── repositories/        работа с базой: только запросы
│   └── subs.py
├── models/              описание таблиц
│   └── subs.py
└── core/
    ├── config.py        настройки из окружения
    └── deps.py          общие зависимости: подключение к базе, текущий юзер

Кто кого вызывает

Правило простое: вызовы идут сверху вниз и никогда обратно.

измерено
routers  →  services  →  repositories  →  база
СлойЧто в нём естьЧего в нём быть не должно
routersразбор запроса, вызов сервиса, код ответаSQL, бизнес-правила
servicesправила: можно ли, сколько раз, что потомSQL, HTTP-коды
repositoriesзапросы к базеправила, HTTP
modelsописание таблицлогика

Нарушение этого порядка — самый частый признак запущенного проекта. Если repositories знает про HTTP-коды, значит, слои перепутаны, и менять такой код придётся везде сразу.

Как находить нужное

Что умеет система — список маршрутов (статья 7.1).

Где правило — в services. Вопрос «почему нельзя подписаться дважды» решается там, а не в обработчике.

Где запрос к базе — в repositories. Если ищешь, откуда берутся данные, иди туда.

Что приходит и уходит — в schemas.

Другие названия того же самого

ЗдесьВ других проектах
routerscontrollers, views, handlers, api
servicesuse_cases, domain, logic, core
repositoriesdao, storage, db, queries
schemasdto, serializers, types

Слова разные, деление то же. Увидев незнакомое имя папки, подставь его в эту таблицу.

Зависимости

Строка, которая встречается в каждом обработчике:

@app.get("/subs/{user_id}")
def get_subs(user_id: int, conn = Depends(get_conn)):

Depends(get_conn) означает: «фреймворк, дай мне подключение к базе». Откуда оно возьмётся и когда закроется — забота фреймворка.

Такие зависимости — обычное место для подключения к базе, проверки токена и текущего пользователя. Если не знаешь, откуда в обработчике взялся аргумент, которого нет в адресе, — ищи Depends или аналог.

Как сказать ИИ

Запрос
Разложи этот проект по слоям: что в роутерах, что в сервисах,
что в работе с базой. Если слои перепутаны — скажи, где именно.
Запрос
Я хочу добавить правило «не больше 10 подписок на пользователя».
В каком слое ему место и какие файлы придётся тронуть?
Код пока не пиши.

Второй запрос — проверка на понимание архитектуры: если ИИ предлагает вписать правило в обработчик, слои в проекте уже поехали.

На что смотреть

SQL в обработчике. Самый частый признак. Запрос переиспользовать нельзя, протестировать без поднятия сервера тоже.

Бизнес-правила в репозитории. if len(cities) > 10 среди SQL — правило спрятано там, где его никто не станет искать.

HTTP-коды в сервисе. raise HTTPException(404) вне роутера означает, что сервис теперь нельзя вызвать ниоткуда, кроме HTTP.

Импорт снизу вверх. repositories импортирует routers — круговая зависимость и путаница.

Папка utils, в которой всё. Признак, что место новому коду искать перестали (статья 2.3).

Шпаргалка

  • Четыре слоя: роуты · сервисы · репозитории · модели.

  • Вызовы идут только сверху вниз.

  • Правило — в сервисах, SQL — в репозиториях, коды ответов — в роутерах.

  • Имена папок разные, деление одинаковое.

  • Аргумент, которого нет в адресе, приходит через Depends.

  • Для маленького проекта слои не нужны — они появляются с размером.