Начните с плоской структуры для небольших скриптовAdded
Размещайте все файлы (main.py, models.py, database.py) в корне проекта. Подходит для прототипов, автоматизаций и сервисов на пару эндпоинтов.
Перейдите к слоистой структуре по техническим ролямAdded
Группируйте файлы по их техническому назначению: models/, schemas/, services/, routers/. Это логичный шаг при выходе за рамки прототипа.
Группируйте код по фичам при росте функциональностиAdded
Создавайте отдельные модули под части предметной области (users/, products/, orders/), размещая слои внутри каждой фичи.
Внедрите гексагональную архитектуру с портами и адаптерамиRecommendedAdded
Отделите бизнес-логику в центр (domain/), описав внешние зависимости через интерфейсы-порты, а базы данных и фреймворки — через адаптеры.
Примените чистую архитектуру для сложной бизнес-логикиRecommendedAdded
Организуйте проект концентрическими слоями: сущности (entities), сценарии использования (use_cases), адаптеры и внешние фреймворки. Зависимости направлены строго внутрь.
Изолируйте модули в формате модульного монолитаRecommendedAdded
Скомбинируйте подход по фичам с жестким ограничением видимости: каждый модуль имеет открытый api.py, а внутренности скрыты в internal/.
Выберите подходящую структуру под требования и масштабы проектаAdded
Оцените размер команды, сложность предметной области и частоту изменений. Начните с более простой структуры и усложняйте её по мере роста.
## Зачем знать вариантыRemoved
## Зачем знать варианты
Мы взяли слоистую структуру. Это осознанный выбор, а не единственный правильный вариант.
Знать остальные надо по трём причинам. Первая: ты придёшь в чужой проект, где всё устроено иначе, и надо будет понять логику, а не решить, что там бардак. Вторая: у каждой структуры есть цена, и лучше знать её заранее. Третья: вопрос про архитектуру задают на каждом собеседовании.
Этот список непоследовательный — можно читать в любом порядке.
Плоская: всё рядомRemoved
```
project/
├── main.py
├── models.py
├── schemas.py
└── database.py
```
Всё в корне, один файл на тип сущности. Именно так выглядит большинство примеров в туториалах.
**Когда годится:** скрипт, прототип, учебный пример, сервис на три эндпоинта.
**Где ломается:** примерно на тридцатом файле. `models.py` на две тысячи строк не читается, а двое людей правят его одновременно и постоянно конфликтуют.
Слоистая: группируем по технической ролиRemoved
```
app/
├── models/ category.py product.py order.py
├── schemas/ category.py product.py order.py
├── repository/ category.py product.py order.py
├── services/ category.py product.py order.py
└── routers/ category.py product.py order.py
```
Папка — это **роль** файла. Все модели вместе, все роутеры вместе.
**Плюсы:** очевидно, куда класть новый файл. Правило зависимостей проверяется глазами. Новичок разбирается за десять минут.
**Цена:** чтобы добавить одну функцию, надо открыть пять папок и править пять файлов. Всё, что относится к заказам, размазано по всему проекту.
По фичам: группируем по предметной областиRemoved
```
app/
├── categories/
│ ├── models.py
│ ├── schemas.py
│ ├── service.py
│ └── router.py
├── products/
│ └── ... то же самое
└── orders/
└── ... то же самое
```
Папка — это **часть предметной области**. Слои никуда не делись — они внутри каждой папки.
**Плюсы:** вся функциональность в одном месте. Удалить фичу — удалить папку. Двое людей работают над разными фичами и не пересекаются.
**Цена:** границы слоёв больше не видны в дереве — их приходится держать договорённостью. Новичок легко напишет SQL в `router.py` — он же рядом.
Так устроен **[Netflix Dispatch](https://github.com/Netflix/dispatch)** и этот подход советует известный сборник **[FastAPI Best Practices](https://github.com/zhanymkanov/fastapi-best-practices)**.
Гексагональная: порты и адаптерыRecommendedRemoved
```
app/
├── domain/ чистая логика, никаких библиотек
│ ├── entities.py
│ └── ports.py интерфейсы: «мне нужен тот, кто умеет сохранять»
└── adapters/ реализации этих интерфейсов
├── postgres.py
├── http.py
└── memory.py для тестов
```
Идея: логика в центре и **не знает ничего** о базе, HTTP и фреймворке. Она объявляет порты — что ей нужно, а адаптеры это предоставляют.
**Плюсы:** логика тестируется без базы вообще. Смена PostgreSQL на что угодно — новый адаптер, логика не меняется.
**Цена:** много интерфейсов и перекладывания данных из одних объектов в другие. На маленьком проекте это чистые накладные расходы.
Первоисточник: **[Алистер Кокберн о гексагональной архитектуре](https://alistair.cockburn.us/hexagonal-architecture/)**
Чистая архитектура: кольца вокруг сущностейRecommendedRemoved
```
app/
├── entities/ сущности и правила предметной области
├── use_cases/ сценарии: «оформить заказ»
├── adapters/ преобразование данных
└── frameworks/ FastAPI, SQLAlchemy, всё внешнее
```
Правило одно: **зависимости всегда указывают внутрь**, к сущностям. Внешнее кольцо знает про внутреннее, никогда наоборот.
**Плюсы:** логика переживёт смену фреймворка и базы. Сценарии читаются как описание бизнеса.
**Цена:** самая высокая из всех. Много слоёв преобразования, больше кода ради того же результата. Оправдана там, где логика сложнее техники — банки, страхование, биллинг.
Первоисточник: **[Роберт Мартин о чистой архитектуре](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)**
Модульный монолит: фичи с границамиRecommendedRemoved
```
app/
├── catalog/
│ ├── api.py единственная точка входа для чужих
│ └── internal/ внутренности, трогать нельзя
└── orders/
├── api.py
└── internal/
```
То же, что группировка по фичам, но с явным правилом: модули общаются **только через объявленный интерфейс**. Заказы не лезут во внутренности каталога, даже если технически могут.
**Плюсы:** готовность разрезать на микросервисы, если потребуется. Границы видны.
**Цена:** дисциплина. Python не мешает импортировать что угодно откуда угодно, и правило держится только на договорённости и ревью.
## Отдельное измерение: src-layout против flat-layoutRemoved
## Отдельное измерение: src-layout против flat-layout
Это **не архитектура**, а вопрос упаковки. Путают постоянно.
```
flat-layout src-layout
project/ project/
├── app/ ├── src/
│ └── ... │ └── app/
└── pyproject.toml └── pyproject.toml
```
**Зачем src:** пакет нельзя случайно импортировать из корня проекта. Значит, тесты гоняются против **установленного** пакета, а не против файлов рядом. Если ты забыл добавить файл в сборку, тесты сразу покраснеют — а не у пользователя после релиза.
**Зачем без src:** короче пути, меньше вложенности. Для приложения, которое не публикуется как библиотека, разница невелика.
Мы выбрали flat с пакетом `app/` — потому что строим приложение, а не библиотеку на PyPI.
Подробно с аргументами с обеих сторон: **[src-layout vs flat-layout](https://packaging.python.org/en/latest/discussions/src-layout-vs-flat-layout/)**
## Что советует сам FastAPIRemoved
## Что советует сам FastAPI
Официальная документация в разделе **[Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/)** показывает вариант ближе к слоистому: пакет `app/` с `routers/`, общими зависимостями и внутренними подпакетами.
Важное: **у FastAPI нет обязательной структуры.** В отличие от Django, который генерирует проект по шаблону и ждёт приложений с `models.py` и `views.py`, FastAPI не навязывает ничего. Отсюда и свобода, и разнобой между проектами.
Поэтому вопрос «как правильно» не имеет ответа в документации — только в контексте твоего проекта.
## Как выбиратьRemoved
## Как выбирать
| Ситуация | Что брать |
|---|---|
| Скрипт, прототип, до 10 файлов | Плоская |
| Учёба, небольшой сервис, до 10 сущностей | Слоистая |
| Много сущностей, команда от трёх человек | По фичам |
| Готовимся разрезать на сервисы | Модульный монолит |
| Логика сложнее техники, долгая жизнь | Гексагон или чистая |
**Три правила, которые важнее выбора:**
1. **Любая структура лучше, чем две сразу.** Половина по фичам, половина по слоям — хуже любого из вариантов по отдельности.
2. **Структура под размер сейчас, а не на вырост.** Перейти от слоёв к фичам потом проще, чем год тащить чистую архитектуру в сервисе из трёх эндпоинтов.
3. **Записанное решение важнее самого решения.** Абзац в README «мы выбрали так-то, потому что...» спасает следующего человека от переписывания всего по своему вкусу.
## Что вынестиRemoved
## Что вынести
Нет «правильной» структуры — есть подходящая под размер проекта, размер команды и срок жизни.
Мы взяли слоистую, потому что на ней видно границы ответственности. Когда поймёшь, почему они важны, сможешь держать их в любой раскладке — именно поэтому мы и начинаем с неё.
И главное: в реальном проекте качество чаще определяется не выбором структуры, а тем, насколько последовательно её держат. Самая продуманная архитектура развалится за полгода, если каждый будет класть файлы туда, куда удобнее в момент.