Если domain-слой не зависит от других слоев, то как он взаимодействует с data-слоем?

В архитектуре с разделением на слои (например, Clean Architecture), domain-слой действительно не должен зависеть ни от data-, ни от presentation-слоя. Это основное правило, обеспечивающее инверсию зависимостей и чистоту бизнес-логики.

Но несмотря на отсутствие прямой зависимости, domain-слой всё равно взаимодействует с data-слоем — через абстракции (интерфейсы), которые определяются в самом domain-слое, а реализуются в data-слое.

🔄 Принцип взаимодействия: инверсия зависимостей

Взаимодействие основано на Dependency Inversion Principle (DIP):

  • Domain зависит от абстракций, а не от конкретных реализаций.

  • Data реализует эти абстракции, но сам ничего не знает про domain.

📌 Как это выглядит на практике

1. В domain слое определяем интерфейс:

interface UserRepository {
suspend fun getUser(id: String): User
}

И используем его в use-case:

class GetUserUseCase(private val userRepository: UserRepository) {
suspend operator fun invoke(id: String): User {
return userRepository.getUser(id)
}
}

🔹 Domain-слой ничего не знает про то, как UserRepository реализован — через Room, Retrofit, Firebase или локальный кэш.

2. В data слое реализуем этот интерфейс:

class UserRepositoryImpl(
private val remoteSource: RemoteDataSource,
private val localSource: LocalDataSource
) : UserRepository {
override suspend fun getUser(id: String): User {
return localSource.getCachedUser(id) ?: remoteSource.fetchUser(id)
}
}

3. Инъекция зависимостей

На уровне App (или presentation) при помощи Dagger/Hilt/Koin/ручной сборки:

val repository: UserRepository = UserRepositoryImpl(remote, local)
val useCase = GetUserUseCase(repository)

Почему так делается

  • Domain остаётся "чистым": его можно тестировать без Android, Retrofit, Room и других внешних зависимостей.

  • Data-слой подменяем: можно заменить реализацию UserRepositoryImpl на заглушку или мок без изменения domain-слоя.

  • Инверсия зависимостей — основной принцип Clean Architecture и Hexagonal Architecture.

📊 Архитектурная схема (упрощённо)

\[Presentation Layer\]

UseCases (domain)

Interfaces (domain)

Реализация интерфейсов
(data layer) 

То есть:

  • Domain задаёт правила (через интерфейсы).

  • Data следует этим правилам, реализуя интерфейсы.

  • Приложение встраивает нужную реализацию через DI.

Таким образом, domain-слой взаимодействует с data-слоем не напрямую, а через интерфейсы, которые сам же и определяет. Это даёт максимальную изоляцию бизнес-логики, улучшает тестируемость и облегчает сопровождение проекта.