Короткий ответ
Нужно явно разделить «инфраструктурные» зависимости (конфигурация, соединения) от бизнес-логики через отдельный слой сервисов/репозиториев, передавать зависимости в сервисы явными параметрами конструктора/функции, а не через скрытые глобальные синглтоны, и решить — хватает ли простого Depends с чёткой дисциплиной или нужен более структурированный DI-подход (например, явный контейнер с управляемым временем жизни объектов).
Как это работает подробнее
Встроенный Depends FastAPI прост и декларативен, но сам по себе не предотвращает скрытые зависимости от глобальных переменных модуля (например, from .db import engine внутри функции сервиса) — эти связи не видны в сигнатуре и не подменяются через dependency_overrides. Полноценный DI-контейнер (например, dependency-injector) даёт более строгий контроль над жизненным циклом объектов (singleton/per-request/factory), но добавляет новый слой абстракции и порог входа для команды.
Чистый Depends с дисциплиной явной передачи зависимостей дешевле в освоении и достаточен для большинства приложений, но требует самодисциплины команды, чтобы не образовывались скрытые связи. Полноценный DI-контейнер даёт более строгие гарантии и управление жизненным циклом объектов при большом масштабе и множестве команд, но увеличивает порог входа и может быть избыточен для сервиса среднего размера. Критерий выбора — размер кодовой базы, число команд, работающих с ней, и частота возникновения проблем со скрытыми зависимостями на практике.
Пример кода
from fastapi import Depends
class OrderService:
def __init__(self, db_session):
self.db_session = db_session
def get_order(self, order_id: int):
return self.db_session.get(Order, order_id)
def get_order_service(db=Depends(get_db)):
return OrderService(db)Что использовать на практике
- Практика. Практичный средний путь — оставить
Dependsкак механизм связывания HTTP-слоя с сервисным слоем, но внутри сервисного слоя требовать явной передачи зависимостей через конструктор/аргументы, избегая прямого импорта глобальных объектов внутри бизнес-логики. Это сохраняет простоту FastAPI и при этом делает сервисы тестируемыми без похода в HTTP-слой.
Подводные камни
- Риск. Вводят полноценный DI-контейнер «на всякий случай» в небольшом приложении, увеличивая сложность без реальной пользы; оставляют скрытые глобальные зависимости внутри сервисов, из-за чего unit-тесты бизнес-логики вынуждены поднимать весь FastAPI app и реальную/фейковую инфраструктуру вместо изолированного теста класса сервиса.