Мобильная разработка

Clean Architecture для мобильных

Два разработчика работают над функцией «добавить в корзину». Первый кладёт логику прямо в ViewModel, делает запрос к Retrofit, обновляет UI. Через шесть месяцев продуктовые требования меняются трижды, появляется оффлайн-режим, нужны тесты. Второй разработчик написал Use Case и Repository с первого дня - каждое изменение занимает час. Clean Architecture - это инвестиция, которая окупается на третьем месяце.

  • **Google рекомендует** Clean Architecture в официальной документации Android (Guide to App Architecture)
  • **Uber, Twitter, Netflix** используют вариации Clean Architecture для своих мобильных приложений
  • **TCA (The Composable Architecture)** - реализация похожих принципов в мире iOS/SwiftUI от создателей Point-Free

Слои Clean Architecture

Небольшой проект начинается просто: Activity делает сетевой запрос, парсит JSON и показывает результат. Через год - 50 тысяч строк кода, Activity знает об API, базе данных, бизнес-правилах и навигации одновременно. Тест написать невозможно. **Clean Architecture** Роберта Мартина решает эту проблему: разбивает код на слои с чёткими границами.

На практике в мобильной разработке часто используют три слоя: **Presentation** (ViewModel + UI), **Domain** (Use Cases + Entities + Repository interfaces), **Data** (Repository реализации + API + локальная БД). Domain - это ядро, которое не зависит ни от Android, ни от внешних библиотек.

**Почему domain - чистый Kotlin?** Когда domain не зависит от Android SDK, его можно тестировать обычными JUnit-тестами без эмулятора. Тесты запускаются за секунды на CI, а не минуты. Это главная практическая выгода от разделения слоёв.

Какой слой Clean Architecture не должен содержать import-ы из Android SDK?

Dependency Rule

**Dependency Rule** - главный закон Clean Architecture: **зависимости кода должны указывать только внутрь**. Внешние слои знают о внутренних. Внутренние слои не знают о внешних. Domain не знает, что данные приходят из Retrofit или Room - он работает только с абстракциями.

Dependency Rule реализуется через **Dependency Inversion Principle**: высокоуровневые модули (Domain) не зависят от низкоуровневых (Data). Оба зависят от абстракций (интерфейсов). Интерфейс `UserRepository` живёт в Domain, реализация - в Data.

**Mapper паттерн:** Data слой возвращает DTO (Data Transfer Object), Domain ожидает Entity. Mapper конвертирует между ними. Mapper живёт в Data, чтобы Domain не знал о форматах API. Никогда не передавайте DTO из Data прямо в Presentation.

Где должен находиться интерфейс Repository в Clean Architecture?

Use Cases / Interactors

**Use Case** (он же Interactor) - класс, реализующий одну конкретную операцию приложения: «войти в систему», «оформить заказ», «получить список продуктов». Use Case оркестрирует репозитории и правила бизнес-логики. ViewModel вызывает Use Case, не зная деталей реализации.

Использование `operator fun invoke()` позволяет вызывать Use Case как функцию: `loginUseCase(email, password)` вместо `loginUseCase.execute(email, password)`. Это популярный Kotlin-идиом для Use Cases.

**Один Use Case - одна ответственность.** Если Use Case делает слишком много - разбейте его. `PlaceOrderUseCase` не должен также обновлять баланс, отправлять email и обновлять UI. Для каждого действия - отдельный Use Case.

Что должен делать Use Case при успешном входе помимо основной операции?

Repository pattern

**Repository** абстрагирует источник данных. Use Case говорит «дай мне пользователя», Repository решает: взять из кеша, из локальной БД или сделать сетевой запрос. Детали хранения и получения данных скрыты за интерфейсом.

Паттерн **Single Source of Truth (SSOT)**: Room - единственный источник правды. Сетевые данные всегда сохраняются в Room, UI наблюдает за Room через Flow. При обновлении сети данные текут: API -> Room -> Flow -> UI. Это устраняет рассинхронизацию между кешем и сервером.

**Fake Repository для тестов:** вместо мока создайте `FakeUserRepository : UserRepository` с in-memory данными. Это позволяет тестировать Use Cases и ViewModels детерминированно, без сетевых запросов и зависимостей от базы данных.

В паттерне Single Source of Truth (SSOT) что является источником правды для UI?

Clean Architecture для мобильных

  • Три слоя: Presentation (ViewModel + UI), Domain (Use Cases + Entities + интерфейсы), Data (реализации + API + БД)
  • Dependency Rule: зависимости только внутрь. Domain - чистый Kotlin, тестируется без Android
  • Use Case = одна бизнес-операция. operator invoke() для Kotlin-идиоматичного вызова. Тест без Android SDK
  • Repository скрывает источник данных. SSOT: API -> Room -> Flow -> UI. FakeRepository для тестов

Связанные темы

Clean Architecture применяется совместно с другими паттернами и инструментами:

  • KMP: Kotlin Multiplatform — Domain слой Clean Architecture идеально переносится в KMP commonMain
  • Android Jetpack и ViewModel — ViewModel - часть Presentation слоя Clean Architecture
  • Тестирование мобильных приложений — Чистый Domain слой делает тестирование Use Cases быстрым и изолированным

Вопросы для размышления

  • Когда Clean Architecture избыточна? При каком размере проекта накладные расходы оправданы?
  • Как Repository паттерн помогает реализовать оффлайн-первый (offline-first) подход в мобильных приложениях?
  • Чем Use Case отличается от метода в сервисном классе? Когда стоит создать отдельный класс Use Case?

Связанные уроки

  • sd-01-intro
Clean Architecture для мобильных

0

1

Войти