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

Как слои работают между собой в чистой архитектуре

2.0 Middle🔥 192 комментариев
#Архитектура и паттерны

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

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

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

Как слои работают между собой в чистой архитектуре

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

Направленность зависимостей и механизмы взаимодействия

Domain Layer (самый внутренний) является абсолютно независимым. Он содержит бизнес-сущности (Entities), Use Cases (Интерфейсы взаимодействия) и репозитории (Repository Interfaces). Этот слой определяет правила и логику приложения.

// Domain Layer: Интерфейс репозитория (зависимость от Domain)
interface UserRepository {
    suspend fun getUserById(id: String): User
}

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

Data Layer зависит от Domain Layer. Он реализует интерфейсы репозиторий, объявленные в доменном слое, и предоставляет конкретные реализации для работы с источниками данных (например, API или локальной базой данных).

// Data Layer: Реализация репозитория (зависит от Domain интерфейса)
class UserRepositoryImpl @Inject constructor(
    private val apiService: ApiService,
    private val userDao: UserDao
) : UserRepository {
    override suspend fun getUserById(id: String): User {
        // Здесь логика получения данных из сети или базы
        val networkUser = apiService.getUser(id)
        userDao.insertUser(networkUser.toEntity())
        return userDao.getUserById(id)?.toDomainEntity()
    }
}

Presentation Layer (например, ViewModel в MVVM) зависит от Domain Layer. Он использует Use Cases для выполнения бизнес-операций и никогда не обращается напрямую к Data Layer.

// Presentation Layer: ViewModel (зависит от Domain Use Case)
class UserViewModel @Inject constructor(
    private val getUserUseCase: GetUserUseCase
) : ViewModel() {
    private val _userState = MutableStateFlow<User?>(null)
    val userState: StateFlow<User?> = _userState.asStateFlow()

    fun loadUser(id: String) {
        viewModelScope.launch {
            _userState.value = getUserUseCase(id)
        }
    }
}

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

  • Инверсия зависимостей (Dependency Inversion Principle): Внешние слои зависят от абстракций (интерфейсов) внутренних слоев. Например, UserRepositoryImpl зависит от абстракции UserRepository, а UserViewModel зависит от абстракции GetUserUseCase.
  • Строгая однонаправленность: Поток данных и вызовов всегда направлен из внешних слоев к внутренним. Presentation Layer вызывает Use Case, Use Case использует интерфейс Repository, а Data Layer предоставляет его реализацию.
  • Изоляция изменений: Изменения в одном слое (например, переход от REST API к GraphQL в Data Layer) минимально затрагивают другие. Domain Layer остается неизменным, а Presentation Layer не знает об этих деталях.
  • Тестирование: Благодаря зависимостям от интерфейсов, каждый слой можно тестировать независимо. Domain Use Cases тестируются с помощью фейковых репозиторий, а Data Layer — с фейковыми API клиентами.

Практическая реализация с Dependency Injection

Для управления этими зависимостями и обеспечения правильного направления связей в Android часто используется Dependency Injection (например, Hilt или Dagger). Компоненты DI собирают реализации из Data Layer и предоставляют их для удовлетворения абстрактных зависимостей Domain и Presentation слоев.

// Модуль Hilt для предоставления зависимостей
@Module
@InstallIn(SingletonComponent::class)
object DataModule {
    @Provides
    @Singleton
    fun provideUserRepository(apiService: ApiService, db: AppDatabase): UserRepository {
        return UserRepositoryImpl(apiService, db.userDao())
    }
}

@Module
@InstallIn(ViewModelComponent::class)
object DomainModule {
    @Provides
    fun provideGetUserUseCase(repository: UserRepository): GetUserUseCase {
        return GetUserUseCase(repository)
    }
}

Таким образом, слои в Чистой Архитектуре работают через четко определенные абстракции и интерфейсы, что обеспечивает высокую модульность, тестируемость и устойчивость к изменениям приложения. Каждый слой имеет свою единственную ответственность, а их взаимодействие контролируется принципами инверсии зависимостей и направленностью от внешних деталей реализации к внутренним бизнес-правилам.

Как слои работают между собой в чистой архитектуре | PrepBro