Что такое верхний и нижний уровни в принципе Dependency Inversion
Что проверяют
Понимаете ли вы, что верхний и нижний уровни это не про расположение файлов и не про то, что интерфейс ближе к пользователю, а про направление смысловой зависимости: политика против механизма. В мобильной разработке вопрос прикладной, ведь речь о том, зависит ли ваш экранный слой от сетевого клиента, хранилища и сторонних SDK или от собственных протоколов.
Как отвечать
Верхний уровень это модули, которые задают правила: сценарии использования, доменная логика, вью-модели и презентеры. Нижний уровень это детали исполнения: сеть, база, связка ключей, файлы, аналитика, конкретная библиотека.
Принцип говорит две вещи. Модули обоих уровней зависят от абстракций, а не друг от друга напрямую. И абстракции не зависят от деталей, детали зависят от абстракций.
Самое важное, что отличает сильный ответ: протокол объявляется на стороне верхнего уровня и формулируется в его словах, а нижний уровень его реализует.
protocol ProfileLoading {
func load(id: String) async throws -> Profile
}
final class ProfileViewModel {
private let loader: ProfileLoading
init(loader: ProfileLoading) { self.loader = loader }
}Поток вызовов идёт сверху вниз, а зависимость исходников снизу вверх: нижний модуль знает про протокол верхнего. Это и есть инверсия. Выгода конкретная: вью-модель тестируется без сети, транспорт меняется без правки экрана, модули собираются раздельно.
Где спотыкаются
Ставят знак равенства между принципом инверсии зависимостей и внедрением зависимостей. Внедрение это способ доставки объекта, принцип это правило о том, кто владеет абстракцией. Можно аккуратно передавать в инициализатор конкретный сетевой класс и принцип при этом нарушать.
Кладут протокол рядом с реализацией, в сетевой модуль. Абстракция формально появилась, но верхний слой по-прежнему импортирует нижний, направление зависимости не изменилось, и раздельная сборка не работает.
Делают протокол зеркалом реализации: методы названы по эндпоинтам, в сигнатурах торчат типы запроса и сырые данные. Абстракция протекает, подменить транспорт без переписывания вызывающего кода не выйдет, а тестовый дубль приходится собирать вручную из ответов сервера.