Если 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-слоем не напрямую, а через интерфейсы, которые сам же и определяет. Это даёт максимальную изоляцию бизнес-логики, улучшает тестируемость и облегчает сопровождение проекта.