Читаем бэкенд-код
роуты, модели, сервисы и кто кого вызывает
Что это
Бэкенд-проект почти всегда разложен по слоям, и слои эти одинаковые везде: маршруты, схемы, бизнес-логика, работа с базой. Разные фреймворки называют их по-разному, но делят одинаково.
Зная деление, в незнакомом проекте находишь нужное за минуту вместо получасового блуждания.
Как выглядит в коде
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.
Другие названия того же самого
| Здесь | В других проектах |
|---|---|
routers | controllers, views, handlers, api |
services | use_cases, domain, logic, core |
repositories | dao, storage, db, queries |
schemas | dto, 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.Для маленького проекта слои не нужны — они появляются с размером.