От кого зависит интерактор в чистой архитектуре
Что проверяют
Поняли ли вы правило зависимостей или выучили названия слоёв. Интерактор это сердце доменного слоя, и по ответу видно, останется ли у вас домен чистым модулем на Kotlin или туда через полгода приедут Context, Retrofit и Room.
Как отвечать
Главный тезис: интерактор не зависит ни от кого снаружи домена. Он знает только сущности своего слоя и интерфейсы, которые сам же и объявляет:
- репозитории: интерфейс лежит в домене, реализация в data и подставляется через Hilt или Koin. Направление зависимости перевёрнуто, поэтому домен не видит ни сети, ни базы;
- другие интеракторы, если операция составная;
- ничего из
android.*: ниContext, ниLiveData, ни моделей Room, ни DTO ретрофита.
Обратное направление тоже важно: презентация зависит от домена, а не наоборот. ViewModel вызывает интерактор и получает результат значением или потоком, а сам интерактор про существование экрана не знает. В каноничной формулировке у него есть выходной порт, но это тоже интерфейс домена, который реализует презентация.
class ObserveCartUseCase(private val cart: CartRepository) {
operator fun invoke(): Flow<Cart> = cart.observe()
}Выбор диспетчера тоже стоит держать за пределами домена: за поток отвечает репозиторий или вызывающая сторона.
Где спотыкаются
Отвечают, что интерактор зависит от UI, потому что «получает данные от пользователя». Данные приходят параметрами вызова, и это не зависимость. Как только домен начинает знать про экран, слои меняются местами и схема теряет смысл.
Тащат в домен фреймворк. Одна модель Room в сигнатуре превращает чистый модуль в андроидный, тестам требуется Robolectric, а замена источника данных перестаёт быть локальной.
Делают интерактор транзитом в одну строку к репозиторию и на вопрос, зачем он нужен, ответить не могут. Смысл появляется там, где есть правило: проверка, склейка нескольких источников, кэш, инвариант.