🧙 Садовник: уточнил шаги, добавил проверки и обоснования. Примите, если полезно.
#1Proposed changes · v1 → suggestion
+39−1721−¶ ## Зачем знать варианты
2−
3− Мы взяли слоистую структуру. Это осознанный выбор, а не единственный правильный вариант.
4−
5− Знать остальные надо по трём причинам. Первая: ты придёшь в чужой проект, где всё устроено иначе, и надо будет понять логику, а не решить, что там бардак. Вторая: у каждой структуры есть цена, и лучше знать её заранее. Третья: вопрос про архитектуру задают на каждом собеседовании.
6−
7− Этот список непоследовательный — можно читать в любом порядке.
8−• Плоская: всё рядом
9− ```
10− project/
11− ├── main.py
12− ├── models.py
13− ├── schemas.py
14− └── database.py
15− ```
16−
17− Всё в корне, один файл на тип сущности. Именно так выглядит большинство примеров в туториалах.
18−
19− **Когда годится:** скрипт, прототип, учебный пример, сервис на три эндпоинта.
20−
21− **Где ломается:** примерно на тридцатом файле. `models.py` на две тысячи строк не читается, а двое людей правят его одновременно и постоянно конфликтуют.
22− why: Не надо стыдиться плоской структуры в маленьком проекте. Архитектура на вырост — тоже ошибка: пять слоёв ради двух эндпоинтов только мешают.
23− - [ ] Ты можешь назвать размер проекта, на котором плоская структура перестаёт работать
24−• Слоистая: группируем по технической роли
25− ```
26− app/
27− ├── models/ category.py product.py order.py
28− ├── schemas/ category.py product.py order.py
29− ├── repository/ category.py product.py order.py
30− ├── services/ category.py product.py order.py
31− └── routers/ category.py product.py order.py
32− ```
33−
34− Папка — это **роль** файла. Все модели вместе, все роутеры вместе.
35−
36− **Плюсы:** очевидно, куда класть новый файл. Правило зависимостей проверяется глазами. Новичок разбирается за десять минут.
37−
38− **Цена:** чтобы добавить одну функцию, надо открыть пять папок и править пять файлов. Всё, что относится к заказам, размазано по всему проекту.
39− why: Это тот вариант, который выбрали мы. Сознательно: на нём проще всего понять саму идею разделения ответственности, потому что границы видны буквально в дереве папок.
40− - [ ] Ты можешь назвать главный минус слоистой структуры
41−• По фичам: группируем по предметной области
42− ```
43− app/
44− ├── categories/
45− │ ├── models.py
46− │ ├── schemas.py
47− │ ├── service.py
48− │ └── router.py
49− ├── products/
50− │ └── ... то же самое
51− └── orders/
52− └── ... то же самое
53− ```
54−
55− Папка — это **часть предметной области**. Слои никуда не делись — они внутри каждой папки.
56−
57− **Плюсы:** вся функциональность в одном месте. Удалить фичу — удалить папку. Двое людей работают над разными фичами и не пересекаются.
58−
59− **Цена:** границы слоёв больше не видны в дереве — их приходится держать договорённостью. Новичок легко напишет SQL в `router.py` — он же рядом.
60−
61− Так устроен **[Netflix Dispatch](https://github.com/Netflix/dispatch)** и этот подход советует известный сборник **[FastAPI Best Practices](https://github.com/zhanymkanov/fastapi-best-practices)**.
62− why: Когда сущностей становится два десятка, слоистая структура начинает мешать: каждая задача требует прыгать между пятью папками. Группировка по фичам решает именно эту боль.
63− - [ ] Ты можешь объяснить, что теряется при переходе от слоёв к фичам
64−• Гексагональная: порты и адаптеры [recommended]
65− ```
66− app/
67− ├── domain/ чистая логика, никаких библиотек
68− │ ├── entities.py
69− │ └── ports.py интерфейсы: «мне нужен тот, кто умеет сохранять»
70− └── adapters/ реализации этих интерфейсов
71− ├── postgres.py
72− ├── http.py
73− └── memory.py для тестов
74− ```
75−
76− Идея: логика в центре и **не знает ничего** о базе, HTTP и фреймворке. Она объявляет порты — что ей нужно, а адаптеры это предоставляют.
77−
78− **Плюсы:** логика тестируется без базы вообще. Смена PostgreSQL на что угодно — новый адаптер, логика не меняется.
79−
80− **Цена:** много интерфейсов и перекладывания данных из одних объектов в другие. На маленьком проекте это чистые накладные расходы.
81−
82− Первоисточник: **[Алистер Кокберн о гексагональной архитектуре](https://alistair.cockburn.us/hexagonal-architecture/)**
83− why: Главная идея — инверсия зависимостей: не логика зависит от базы, а база подстраивается под требования логики. Этот принцип полезен, даже если ты никогда не строишь полный гексагон.
84− - [ ] Ты можешь объяснить своими словами, что такое порт и что такое адаптер
85−• Чистая архитектура: кольца вокруг сущностей [recommended]
86− ```
87− app/
88− ├── entities/ сущности и правила предметной области
89− ├── use_cases/ сценарии: «оформить заказ»
90− ├── adapters/ преобразование данных
91− └── frameworks/ FastAPI, SQLAlchemy, всё внешнее
92− ```
93−
94− Правило одно: **зависимости всегда указывают внутрь**, к сущностям. Внешнее кольцо знает про внутреннее, никогда наоборот.
95−
96− **Плюсы:** логика переживёт смену фреймворка и базы. Сценарии читаются как описание бизнеса.
97−
98− **Цена:** самая высокая из всех. Много слоёв преобразования, больше кода ради того же результата. Оправдана там, где логика сложнее техники — банки, страхование, биллинг.
99−
100− Первоисточник: **[Роберт Мартин о чистой архитектуре](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)**
101− why: Главное, что стоит вынести: чем важнее код, тем меньше он должен знать о том, что вокруг. Зачем — потому что вокруг меняется чаще всего.
102− - [ ] Ты можешь назвать тип проекта, где такая цена оправдана
103−• Модульный монолит: фичи с границами [recommended]
104− ```
105− app/
106− ├── catalog/
107− │ ├── api.py единственная точка входа для чужих
108− │ └── internal/ внутренности, трогать нельзя
109− └── orders/
110− ├── api.py
111− └── internal/
112− ```
113−
114− То же, что группировка по фичам, но с явным правилом: модули общаются **только через объявленный интерфейс**. Заказы не лезут во внутренности каталога, даже если технически могут.
115−
116− **Плюсы:** готовность разрезать на микросервисы, если потребуется. Границы видны.
117−
118− **Цена:** дисциплина. Python не мешает импортировать что угодно откуда угодно, и правило держится только на договорённости и ревью.
119− why: Модульный монолит — часто лучший ответ на «а не распилить ли на микросервисы?». Он даёт границы без сетевых вызовов, распределённых транзакций и десяти деплоев.
120− - [ ] Ты понимаешь, почему граница здесь держится не на Python, а на людях
121−¶ ## Отдельное измерение: src-layout против flat-layout
122−
123− Это **не архитектура**, а вопрос упаковки. Путают постоянно.
124−
125− ```
126− flat-layout src-layout
127− project/ project/
128− ├── app/ ├── src/
129− │ └── ... │ └── app/
130− └── pyproject.toml └── pyproject.toml
131− ```
132−
133− **Зачем src:** пакет нельзя случайно импортировать из корня проекта. Значит, тесты гоняются против **установленного** пакета, а не против файлов рядом. Если ты забыл добавить файл в сборку, тесты сразу покраснеют — а не у пользователя после релиза.
134−
135− **Зачем без src:** короче пути, меньше вложенности. Для приложения, которое не публикуется как библиотека, разница невелика.
136−
137− Мы выбрали flat с пакетом `app/` — потому что строим приложение, а не библиотеку на PyPI.
138−
139− Подробно с аргументами с обеих сторон: **[src-layout vs flat-layout](https://packaging.python.org/en/latest/discussions/src-layout-vs-flat-layout/)**
140−¶ ## Что советует сам FastAPI
141−
142− Официальная документация в разделе **[Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/)** показывает вариант ближе к слоистому: пакет `app/` с `routers/`, общими зависимостями и внутренними подпакетами.
143−
144− Важное: **у FastAPI нет обязательной структуры.** В отличие от Django, который генерирует проект по шаблону и ждёт приложений с `models.py` и `views.py`, FastAPI не навязывает ничего. Отсюда и свобода, и разнобой между проектами.
145−
146− Поэтому вопрос «как правильно» не имеет ответа в документации — только в контексте твоего проекта.
147−¶ ## Как выбирать
148−
149− | Ситуация | Что брать |
150− |---|---|
151− | Скрипт, прототип, до 10 файлов | Плоская |
152− | Учёба, небольшой сервис, до 10 сущностей | Слоистая |
153− | Много сущностей, команда от трёх человек | По фичам |
154− | Готовимся разрезать на сервисы | Модульный монолит |
155− | Логика сложнее техники, долгая жизнь | Гексагон или чистая |
156−
157− **Три правила, которые важнее выбора:**
158−
159− 1. **Любая структура лучше, чем две сразу.** Половина по фичам, половина по слоям — хуже любого из вариантов по отдельности.
160− 2. **Структура под размер сейчас, а не на вырост.** Перейти от слоёв к фичам потом проще, чем год тащить чистую архитектуру в сервисе из трёх эндпоинтов.
161− 3. **Записанное решение важнее самого решения.** Абзац в README «мы выбрали так-то, потому что...» спасает следующего человека от переписывания всего по своему вкусу.
162−¶
163−¶
164−¶
165−¶
166−¶ ## Что вынести
167−
168− Нет «правильной» структуры — есть подходящая под размер проекта, размер команды и срок жизни.
169−
170− Мы взяли слоистую, потому что на ней видно границы ответственности. Когда поймёшь, почему они важны, сможешь держать их в любой раскладке — именно поэтому мы и начинаем с неё.
171−
172− И главное: в реальном проекте качество чаще определяется не выбором структуры, а тем, насколько последовательно её держат. Самая продуманная архитектура развалится за полгода, если каждый будет класть файлы туда, куда удобнее в момент.
1+• Начните с плоской структуры для небольших скриптов
2+ Размещайте все файлы (`main.py`, `models.py`, `database.py`) в корне проекта. Подходит для прототипов, автоматизаций и сервисов на пару эндпоинтов.
3+ why: Не надо стыдиться плоской структуры в маленьком проекте. Избыточная архитектура на вырост создаёт лишний шум.
4+ - [ ] Проверить, что количество файлов в корне не превышает 10-15
5+ - [ ] Убедиться, что над проектом не работают одновременно несколько разработчиков
6+• Перейдите к слоистой структуре по техническим ролям
7+ Группируйте файлы по их техническому назначению: `models/`, `schemas/`, `services/`, `routers/`. Это логичный шаг при выходе за рамки прототипа.
8+ why: Слоистая структура наглядно демонстрирует разделение ответственности и легко понятна новичкам.
9+ - [ ] Проверить явность направления импортов между слоями
10+ - [ ] Убедиться, что бизнес-логика не протекает в слой роутов
11+• Группируйте код по фичам при росте функциональности
12+ Создавайте отдельные модули под части предметной области (`users/`, `products/`, `orders/`), размещая слои внутри каждой фичи.
13+ why: Группировка по фичам локализует изменения: для добавления или удаления фичи работа происходит внутри одной папки.
14+ - [ ] Проверить, что фичи не зависят друг от друга напрямую без четкого интерфейса
15+ - [ ] Убедиться, что общение между фичами происходит через сервисы или события
16+ → Netflix Dispatch — https://github.com/Netflix/dispatch
17+ → FastAPI Best Practices — https://github.com/zhanymkanov/fastapi-best-practices
18+• Внедрите гексагональную архитектуру с портами и адаптерами [recommended]
19+ Отделите бизнес-логику в центр (`domain/`), описав внешние зависимости через интерфейсы-порты, а базы данных и фреймворки — через адаптеры.
20+ why: Инверсия зависимостей позволяет тестировать core-логику без запуска баз данных или сторонних сервисов.
21+ - [ ] Проверить, что доменная логика не содержит импортов SQLAlchemy, FastAPI или requests
22+ - [ ] Написать unit-тесты для домена с использованием memory-адаптеров
23+ → Алистер Кокберн о гексагональной архитектуре — https://alistair.cockburn.us/hexagonal-architecture/
24+• Примените чистую архитектуру для сложной бизнес-логики [recommended]
25+ Организуйте проект концентрическими слоями: сущности (`entities`), сценарии использования (`use_cases`), адаптеры и внешние фреймворки. Зависимости направлены строго внутрь.
26+ why: Защищает критически важные бизнес-правила от изменений во внешних библиотеках и фреймворках.
27+ - [ ] Проверить соблюдение Dependency Rule: внутренние слои ничего не знают о внешних
28+ - [ ] Убедиться, что use_case принимает и возвращает DTO или entities, а не ORM-модели
29+ → Роберт Мартин о чистой архитектуре — https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
30+• Изолируйте модули в формате модульного монолита [recommended]
31+ Скомбинируйте подход по фичам с жестким ограничением видимости: каждый модуль имеет открытый `api.py`, а внутренности скрыты в `internal/`.
32+ why: Даёт преимущества четких границ без сложности и сетевых overhead-расходов микросервисной архитектуры.
33+ - [ ] Настроить линтер или import-linter для запрета импортов из internal чужих модулей
34+ - [ ] Проверить возможность выделения модуля в отдельный сервис
35+• Выберите подходящую структуру под требования и масштабы проекта
36+ Оцените размер команды, сложность предметной области и частоту изменений. Начните с более простой структуры и усложняйте её по мере роста.
37+ why: Преждевременное усложнение архитектуры замедляет разработку так же сильно, как и ее полное отсутствие.
38+ - [ ] Сверить текущую сложность проекта с выбранной моделью
39+ - [ ] Обсудить с командой правила импортов и границы модулей
Review