сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · FastAPI, Django и API

Чистая архитектура в Python

Чистая архитектура: зависимости внутрь

Когда сервис перерастает один файл, встаёт вопрос, как разложить код, чтобы бизнес-логику можно было тестировать и менять фреймворк, не переписывая всё. Собес на middle+ проверяет, понимаешь ли ты слои и направление зависимостей, и, что важнее, чувствуешь ли, где это оверинжиниринг.

Типовые формулировки: «зачем репозиторий и сервисный слой?», «что такое инверсия зависимостей на практике?», «когда слои - перебор».

Слои и направление зависимостей

Идея одна: домен независим от фреймворка и БД, а зависимости направлены ВНУТРЬ - инфраструктура знает о домене, но не наоборот. Тогда можно сменить FastAPI на что угодно или БД на другую, не трогая ядро бизнес-правил.

Инструменты. Репозиторий прячет доступ к данным за интерфейсом - логика не знает про SQL или конкретный ORM. Сервисный слой держит бизнес-сценарии вне HTTP, поэтому эндпоинт становится тонким: распарсил запрос, вызвал сервис, вернул ответ. DTO (data transfer object) (схемы обмена) развязывают внешний контракт API и внутреннюю доменную модель, чтобы изменение одного не ломало другое.

// Импортировать FastAPI в доменном слое - значит привязать ядро к транспорту; ровно то, от чего слои и защищают.

репозиторий
абстракция доступа к данным над ORM/SQL
сервисный слой
бизнес-сценарии вне HTTP-обработчиков

Инверсия зависимостей и её цена

Инверсия зависимостей: логика зависит не от конкретной реализации, а от абстракции - Protocol или ABC (abstract base class), а саму реализацию внедряют извне. Тогда в тесте вместо настоящего репозитория с БД подставляешь заглушку в памяти, и бизнес-логика тестируется без базы, быстро и изолированно. Это главный практический выигрыш слоёв - тестируемость.

Но у слоёв есть цена - церемонии. На простом CRUD полный набор репозиториев, сервисов и DTO избыточен: пишешь три файла там, где хватило бы одного эндпоинта. Слои вводят ПО МЕРЕ роста сложности, а не с первой строки, иначе платишь скоростью и читаемостью за архитектуру, которая пока не нужна.

// Признак зрелости на собесе - назвать не только плюсы слоёв, но и честно сказать, где они лишние.

инверсия зависимостей
зависеть от интерфейса, реализацию внедрять
DTO
схема обмена, отдельная от доменной модели

Как отвечать: «Зачем репозиторий, если можно вызвать ORM прямо в эндпоинте?»

Ради тестируемости и свободы менять инфраструктуру. Репозиторий прячет доступ к данным за интерфейсом, поэтому бизнес-логика не знает, SQL там, ORM или вообще внешний сервис, а в тесте я подменяю его заглушкой в памяти и проверяю логику без поднятой БД, быстро. Плюс если ORM или схема поменяются, правка локализована в репозитории, а не размазана по эндпоинтам. Но я честно смотрю на сложность: на простом CRUD это оверинжиниринг, там ORM прямо во вью нормально. Слои я ввожу, когда логика перерастает тонкий обработчик.

Названа главная выгода (тест без БД через подмену), добавлена локализация изменений и - ключевое для зрелости - оговорка про оверинжиниринг на CRUD; это баланс, а не догма.

На чём валят

  • Держать бизнес-логику в теле эндпоинта: её не переиспользовать и тяжело тестировать без веба.
  • Импортировать фреймворк (FastAPI) в доменном слое - привязывает ядро к транспорту.
  • Разворачивать все слои на простом CRUD: оверинжиниринг ценой скорости и читаемости.
  • Путать DTO и доменную модель: тащить схему API внутрь домена, и внешний контракт начинает диктовать логику.

Проверьте себя

Пять вопросов из банка по этой подтеме. Всего их 15, остальные разбираются в тренажёре.

  1. #clean_architecture1 / 5
    Основная идея слоистой (чистой) архитектуры приложения — какая?
    A)Разложить весь код по как можно большему числу папок для внешней аккуратности
    B)Собрать всю логику в одном большом модуле, чтобы ничего не искать по проекту
    C)Убрать слой базы данных полностью, храня состояние приложения только в памяти
    D)Отделить бизнес-логику от фреймворка и БД, чтобы менять их независимо
    показать ответ и разбор
    +D)Отделить бизнес-логику от фреймворка и БД, чтобы менять их независимо

    // разбор: Слоистая архитектура держит бизнес-правила (домен) в центре, независимыми от деталей: веб-фреймворка, БД, внешних API. Зависимости направлены внутрь — инфраструктура знает о домене, а не наоборот. Тогда FastAPI можно заменить, БД — сменить, а ядро логики останется нетронутым и тестируемым без всего этого окружения.

  2. #clean_architecture2 / 5
    Что даёт паттерн «репозиторий»?
    A)Прячет доступ к данным за интерфейсом — логика не знает про SQL и ORM
    B)Ускоряет запросы к базе данных, автоматически добавляя кеш поверх каждого обращения
    C)Генерирует классы моделей автоматически на основе структуры существующих таблиц
    D)Заменяет базу данных, храня все объекты предметной области прямо в оперативной памяти
    показать ответ и разбор
    +A)Прячет доступ к данным за интерфейсом — логика не знает про SQL и ORM

    // разбор: Репозиторий — объект-коллекция для доменных сущностей: get, add, list, delete — за этим интерфейсом скрыто, что внутри SQLAlchemy, сырой SQL, внешний API или словарь в памяти. Бизнес-логика работает с абстракцией, не зная деталей хранения. Это упрощает тесты (подставить in-memory репозиторий) и смену хранилища.

  3. #clean_architecture3 / 5
    Зачем выделять сервисный слой (use cases) между эндпоинтами и данными?
    A)Собрать бизнес-логику отдельно от HTTP — переиспользуема и тестируема без веба
    B)Для автоматической генерации документации OpenAPI по бизнес-операциям приложения
    C)Чтобы напрямую вызывать SQL из обработчиков, минуя все промежуточные слои
    D)Чтобы эндпоинты стали длиннее и подробнее описывали каждый шаг обработки запроса
    показать ответ и разбор
    +A)Собрать бизнес-логику отдельно от HTTP — переиспользуема и тестируема без веба

    // разбор: Сервисный слой держит сценарии предметной области (оформить заказ, начислить бонус) отдельно от HTTP-обработчиков. Эндпоинт тогда тонкий: распарсил вход, вызвал сервис, вернул результат. Логику можно переиспользовать (из CLI, воркера, другого эндпоинта) и тестировать без поднятия веб-стека. Толстые обработчики с логикой внутри — то, чего избегают.

  4. #clean_architecture4 / 5
    Принцип инверсии зависимостей: как его выражают в Python-коде?
    A)Логика зависит от абстракции (Protocol/ABC), а конкретную реализацию внедряют извне
    B)Каждый модуль сам импортирует и создаёт нужные ему конкретные классы прямо внутри себя
    C)Порядок импортов в файлах выстраивают строго от низких модулей к высоким
    D)Все зависимости объявляют глобальными переменными на уровне пакета приложения
    показать ответ и разбор
    +A)Логика зависит от абстракции (Protocol/ABC), а конкретную реализацию внедряют извне

    // разбор: Инверсия зависимостей (буква D в SOLID): высокоуровневая логика не зависит от низкоуровневых деталей — обе зависят от абстракции. В Python абстракцию задают Protocol или ABC (интерфейс репозитория, шлюза), сервис принимает её в конструкторе, а конкретную реализацию (SQLAlchemy-репозиторий) подставляют при сборке. Ядро не импортирует инфраструктуру.

  5. #clean_architecture5 / 5
    Зачем разделять доменную модель и DTO (схему запроса/ответа)?
    A)Внешний контракт API не диктует внутреннюю модель — их можно менять раздельно
    B)DTO работают быстрее доменных объектов, поэтому их используют вместо моделей везде
    C)DTO нужны только для базы данных, а доменная модель — только для веб-слоя
    D)Это просто дублирование ради формальности, никакой практической пользы в нём нет
    показать ответ и разбор
    +A)Внешний контракт API не диктует внутреннюю модель — их можно менять раздельно

    // разбор: DTO (Pydantic-схемы на входе/выходе) описывают контракт с внешним миром, доменная модель — бизнес-сущность с правилами. Разделив их, вы меняете форму API, не трогая логику, и наоборот; не утекают внутренние поля; валидация транспорта отделена от инвариантов домена. Цена — маппинг между ними, оправданный в нетривиальных системах.

дальше

Теорию прочитали. Навык ставится повторением

В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.