← Назад к вопросам

Какие зависимости должны быть между слоями в чистой архитектуре

3.0 Senior🔥 181 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Зависимости между слоями в чистой архитектуре

В чистой архитектуре (Clean Architecture), предложенной Робертом Мартином, зависимости между слоями строго регламентированы и направлены внутрь, к самым стабильным и абстрактным компонентам системы. Это обеспечивает независимость бизнес-логики от внешних деталей (фреймворков, БД, UI) и облегчает тестирование, поддержку и эволюцию приложения. На Android типичная реализация включает три основных слоя: Presentation (UI), Domain (Бизнес-логика) и Data (Данные).

Направление зависимостей: правило зависимостей

Ключевое правило: внутренние слои не должны знать о внешних. Зависимости направлены от внешних слоёв (менее стабильных, например, UI или инфраструктура) к внутренним (более стабильным, например, бизнес-правилам). Это достигается за счёт инверсии зависимостей (Dependency Inversion Principle, DIP) из SOLID: внутренние слои определяют абстракции (интерфейсы), а внешние слои реализуют их.

Слои и их зависимости

1. Domain Layer (Внутренний слой)

  • Содержит бизнес-логику: Use Cases (Interactors), сущности (Entities), репозитории интерфейсы.
  • Не зависит ни от каких других слоёв (самый стабильный).
  • Определяет абстракции (интерфейсы репозиториев), которые будут реализованы во внешних слоях.
// Domain Layer: интерфейс репозитория (абстракция)
interface UserRepository {
    suspend fun getUser(id: String): User
}

// Domain Layer: Use Case (зависит только от абстракции репозитория)
class GetUserUseCase(private val userRepository: UserRepository) {
    suspend operator fun invoke(id: String): User = userRepository.getUser(id)
}

2. Data Layer (Внешний слой)

  • Реализует источники данных: локальные (БД, SharedPreferences) и удалённые (сетевые API).
  • Зависит от Domain Layer, так как реализует его интерфейсы (например, UserRepository).
  • Не должен влиять на бизнес-логику; изменения в API или БД затрагивают только этот слой.
// Data Layer: реализация репозитория (зависит от абстракции Domain)
class UserRepositoryImpl @Inject constructor(
    private val apiService: ApiService,
    private val userDao: UserDao
) : UserRepository { // Реализует интерфейс из Domain
    override suspend fun getUser(id: String): User {
        // Сетевая логика, кеширование и т.д.
    }
}

3. Presentation Layer (UI, Внешний слой)

  • Содержит UI-компоненты: Activity, Fragment, ViewModel, Compose.
  • Зависит от Domain Layer через Use Cases, но не зависит от Data Layer.
  • Получает данные от Use Cases и отображает их; не содержит бизнес-логики.
// Presentation Layer: ViewModel (зависит от Use Case из Domain)
class UserViewModel @Inject constructor(
    private val getUserUseCase: GetUserUseCase // Зависимость от Domain
) : ViewModel() {
    fun loadUser(id: String) {
        viewModelScope.launch {
            val user = getUserUseCase(id) // Вызов Use Case
            // Обновление UI состояния
        }
    }
}

Ключевые принципы организации зависимостей

  • Инверсия зависимостей (DIP): Domain Layer определяет контракты (интерфейсы), а Data Layer их реализует. Это позволяет Domain Layer оставаться независимым от деталей реализации.
  • Односторонняя зависимость: Presentation → Domain ← Data. Presentation и Data не знают друг о друге.
  • Использование Dependency Injection (DI): Для внедрения реализаций (например, UserRepositoryImpl) в Use Cases или ViewModel, что уменьшает связанность. Популярные инструменты: Dagger/Hilt, Koin.

Схема зависимостей

Presentation Layer (UI)
         ↓
Domain Layer (Use Cases, Entities, Interfaces)
         ↑
Data Layer (Repositories, API, Database)

Практические примеры нарушения зависимостей

  1. Использование Android-зависимостей в Domain Layer (например, Context или классы из Android SDK) — это нарушение, так как Domain должен быть платформонезависимым.
  2. Прямая зависимость Presentation Layer от Data Layer, например, ViewModel, использующий UserRepositoryImpl напрямую вместо интерфейса UserRepository. Это усложняет тестирование и делает систему хрупкой.

Вывод

Правильные зависимости между слоями в чистой архитектуре гарантируют, что ядро приложения (бизнес-логика) остаётся изолированным и тестируемым. За счёт инверсии зависимостей достигается гибкость: вы можете менять UI или источники данных, не затрагивая Domain Layer. Это особенно важно для долгосрочной поддержки и масштабирования Android-приложений.

Какие зависимости должны быть между слоями в чистой архитектуре | PrepBro