Как разбить задумку на части
три вопроса и одно правило направления
Что это
Архитектура звучит солидно, но на деле это ответ на один вопрос: что от чего зависит. Не «какие папки завести» — папки появятся потом, сами, — а кто кого имеет право трогать.
Пока проект маленький, это кажется лишним: один файл, всё работает. Проблема не в размере, а в том, что без ответа на этот вопрос каждый новый ответ ИИ кладёт код туда, где он оказался удобен сейчас. Через месяц в проекте три места, где обращаются к базе, два разных способа читать настройки и функция на двести строк, которую никто не хочет трогать.
Как выглядит в коде
Три вопроса к любой задумке
Прежде чем просить код, ответь на три вопроса. Не ИИ — себе.
1. Что хранится? Перечисли вещи: город, пользователь, заметка, заказ, подписка. Это существительные, и из них потом получатся таблицы и модели данных.
2. Что происходит? Перечисли действия: добавить город, обновить погоду, отправить уведомление. Это глаголы, и из них получатся функции.
3. Что видно снаружи? Экран, страница, команда бота, адрес API. Это то, через что с программой разговаривают.
Три списка на полстраницы — и у проекта уже есть скелет. Приём работает и для бота на двести строк, и для приложения на двадцать экранов.
Правило направления
Дальше эти три списка раскладываются по слоям, и между слоями действует единственное правило, которое стоит помнить:
Верхний слой знает о нижнем. Нижний о верхнем — нет. Экран обращается к логике, логика к данным. Обратно — никогда.
Это не теория и не вкусовщина: нарушение правила видно сразу, потому что программа перестаёт запускаться. Вот два файла, которые знают друг о друге:
# app/models.py
from app.services import fetch_weather
class City:
def refresh(self):
self.temp = fetch_weather(self.name)# app/services.py
from app.models import City
def all_cities():
return [City("Oslo"), City("Bergen")]Запуск:
ImportError: cannot import name 'fetch_weather' from partially initialized
module 'app.services' (most likely due to a circular import)Круговой импорт — это сообщение о том, что слои перепутаны. Данные полезли наверх, в логику. Лечение не в хитром импорте внутри функции (так советуют в интернете, и так часто предлагает ИИ), а в развороте зависимости: модель не знает ничего, сервис знает о модели.
# app/models.py — ничего не импортирует
class City:
def __init__(self, name, temp=None):
self.name = name
self.temp = temp# app/services.py — знает о модели
from app.models import City
def all_cities():
return [City(n, fetch_weather(n)) for n in ("Oslo", "Bergen")][City(Oslo, 4), City(Bergen, 4)]Чем мельче, тем лучше — до предела
Разбивать имеет смысл до того уровня, где каждый кусок можно объяснить одной фразой. «Берёт город, возвращает температуру». «Хранит список городов пользователя». Как только для объяснения нужно слово «и» трижды — пора делить дальше.
Обратный предел тоже есть: двадцать файлов по десять строк — это не архитектура, а россыпь. Признак, что перестарался: чтобы понять одно действие, приходится открыть пять файлов подряд.
Как сказать ИИ
Главная ошибка — просить приложение целиком. Вместо этого разбиение стоит получить текстом, до кода:
Задумка: <опиши в двух-трёх предложениях>.
Не пиши код. Разложи на части:
1. Что хранится — список сущностей с полями.
2. Что происходит — список действий.
3. Что видно снаружи — экраны или адреса.
Для каждого действия укажи, какие сущности оно трогает.Ответ на такой запрос читается за минуту, а исправляется за две. Код, который пришлось бы переписывать, — за вечер.
Вот моё разбиение: <три списка>.
Предложи структуру слоёв: что от чего зависит.
Нарисуй стрелками, кто кого импортирует.
Если где-то получается стрелка в обе стороны — скажи об этом.Я получил ImportError с упоминанием circular import.
Не предлагай перенести импорт внутрь функции.
Объясни, какой из двух модулей не должен знать о другом,
и как развернуть зависимость.На что смотреть
Приложение одним куском. Попросили «сделай приложение» — получили один файл на четыреста строк, где всё вперемешку.
Круговые импорты. Разворачивать зависимость, а не прятать импорт.
Запрос к базе прямо в обработчике экрана. Интерфейс полез через голову логики сразу в данные. Работает — до первого изменения в базе.
Одно и то же действие в трёх местах. Три места, где форматируется температура, и все три надо не забыть поправить.
Файл с именем utils растёт. Туда складывают всё, чему не нашлось места.
Это не слой, а шкаф, в который сметают.
Разбиение придумал ИИ, а ты его не читал. Структуру проекта стоит понимать целиком — это единственная часть, которую нельзя делегировать (статья 1.1).
Шпаргалка
Архитектура — это ответ на вопрос «что от чего зависит».
Три списка до кода: что хранится, что происходит, что видно снаружи.
Три слоя: интерфейс → логика → данные. Стрелки только вниз.
Круговой импорт — сигнал о перепутанных слоях, а не ошибка Python.
Кусок правильного размера объясняется одной фразой без слова «и».
Проси у ИИ разбиение текстом до кода — исправлять текст дешевле.
Папка
utils— место, куда сметают то, что не разложили.