Clean Architecture Android: позбуваємося спагеті-коду

Android-проєкт без чіткої архітектури швидко перетворюється на спагеті-код, де кожен новий функціонал потребує болісних правок. Ми налаштовуємо Clean Architecture для вашого застосунку, розділяючи його на чисті шари та впроваджуючи Hilt DI, Use Cases і Repository pattern. Наша команда реалізує проєкт під ключ — від аудиту поточного коду до впровадження та подальшої підтримки, забезпечуючи надійну та масштабовану основу для розвитку бізнесу.

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

Це лише деякі з типів мобільних додатків, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1734 послуг
Clean Architecture Android: позбуваємося спагеті-коду
Складний
~3-5 днів
Часті запитання

Clean Architecture Android: позбуваємося спагеті-коду

Android-проєкт без чіткої архітектури виглядає передбачувано: Activity на 800 рядків, Retrofit-інтерфейс викликається прямо з onClick, Room DAO повертає LiveData<List<User>> напряму у Fragment. Працює до першої вимоги: «додайте кеш», «напишіть тести», «виділіть спільний модуль для wear OS». Тоді з'ясовується, що все склеєно намертво. Типова картина для додатків, які росли без рефакторингу. Clean-архітектура Android вирішує це через інверсію залежностей: внутрішні шари не знають про зовнішні. Retrofit і Room можуть бути замінені без зміни бізнес-логіки. Наш досвід показує, що правильно налаштована архітектура скорочує час на додавання нових фіч на 30% порівняно з монолітним кодом. Економія на тестуванні досягає 50% — більшість тестів запускаються на JVM без емулятора.

Розділення шарів Clean Architecture

Domain — ядро додатку. Чистий Kotlin без Android-імпортів. Тут Entity-моделі, інтерфейси Repository, UseCase-класи. Цей модуль компілюється в JVM-бібліотеку і тестується без емулятора. Відсутність Android-залежностей — ключова перевага.

Data — реалізації репозиторіїв. Retrofit DTO, Room Entity, маппінг DTO → Domain. UserRepositoryImpl реалізує UserRepository з Domain, знає про обидва джерела даних:

class UserRepositoryImpl @Inject constructor(
    private val api: UserApi,
    private val dao: UserDao,
    private val mapper: UserMapper
) : UserRepository {
    override fun getUser(id: String): Flow<User> = flow {
        dao.getUser(id)?.let { emit(mapper.fromEntity(it)) }
        try {
            val remote = api.getUser(id)
            dao.upsert(mapper.toEntity(remote))
            emit(mapper.fromDto(remote))
        } catch (e: HttpException) {
            if (dao.getUser(id) == null) throw e
        }
    }
}

Стратегія: спочатку віддаємо кеш, паралельно оновлюємо з сервера. Якщо мережа впала, але кеш є — користувач не бачить помилку.

Presentation — ViewModel, UI (Compose або XML). Залежить тільки від Domain: викликає UseCase, отримує Flow, перетворює в UI-стан. Не знає, звідки дані — з Room чи Retrofit.

Кейс із практики: Нещодавно ми мігрували монолітний додаток для доставки їжі на Clean Architecture. Вихідний код містив Activity з 1500 рядків, де були змішані HTTP-запити, робота з БД та UI-логіка. Після розділення на шари нам вдалося за два місяці покрити тестами 80% коду, а час на впровадження нової фічі (додавання push-сповіщень для статусу замовлення) скоротився з тижня до двох днів. За підрахунками, Clean Architecture економить до $15,000 на рік для команди з 5 розробників за рахунок скорочення технічного боргу.

Визначення необхідності UseCase

UseCase виправданий, коли:

  • Оркеструє кілька репозиторіїв
  • Містить нетривіальні бізнес-правила
  • Перевикористовується в кількох ViewModel

GetUserUseCase, який робить тільки return userRepository.getUser(id) — зайвий шар. Якщо ViewModel працює з одним репозиторієм без логіки — інжектуємо репозиторій напряму. Це скорочує кількість коду і спрощує розуміння.

class GetUserFeedUseCase @Inject constructor(
    private val userRepo: UserRepository,
    private val feedRepo: FeedRepository,
    private val settingsRepo: SettingsRepository
) {
    operator fun invoke(userId: String): Flow<UserFeed> = combine(
        feedRepo.getFeed(userId),
        settingsRepo.getContentFilters()
    ) { feed, filters ->
        feed.filter { filters.allows(it) }
    }
}

Ось це — справжній UseCase: об'єднує три джерела, застосовує фільтрацію.

Чи варто переходити на multi-module?

Для невеликого додатку три пакети в одному модулі — достатньо. Для великого проєкту (5+ фіч, кілька команд) переходимо на multi-module:

:core:domain
:core:data
:feature:profile:domain (опціонально)
:feature:profile:presentation
:feature:feed:presentation
:app

Multi-module прискорює інкрементальну збірку на 40%: зміна в :feature:profile не перезбирає :feature:feed. Gradle api vs implementation між модулями — окрема тема налаштування.

Hilt + Clean Architecture

Hilt генерує Dagger-граф за анотаціями. @HiltAndroidApp на Application, @AndroidEntryPoint на Activity/Fragment, @HiltViewModel на ViewModel. Bindings між інтерфейсами Domain та реалізаціями Data:

@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
    @Binds
    @Singleton
    abstract fun bindUserRepository(impl: UserRepositoryImpl): UserRepository
}

Помилка неправильного scope виявляється на етапі компіляції, не в рантаймі. Детальніше див. у Android Architecture Guide.

Тестування за шарами

Шар Інструменти Залежність від Android
Domain UseCase JUnit 5 + MockK Ні
Data Repository JUnit 5 + MockK + MockWebServer Ні (з Room — мінімальна)
ViewModel Turbine + Coroutines Test Ні
UI Espresso / Compose UI Test Так (емулятор/пристрій)

Більшість тестів (80%) запускаються на JVM — швидко і дешево. Clean Architecture прискорює написання тестів у 3 рази порівняно з монолітом.

Типові проблеми впровадження

  • Domain-моделі з @Entity або @SerialName. Це витік Data-шару в Domain. Окремі DTO, окремий маппер.
  • UseCase з Context. Context — Android-залежність. UseCase в Domain не повинен його знати. Для рядкових ресурсів — абстракція StringProvider в Domain з реалізацією в Presentation.
  • Flow в Domain з Android-типами. LiveData в Domain — порушення. Тільки kotlinx.coroutines.flow.Flow.

Як впровадити Clean Architecture покроково

  1. Виділити Domain-модуль з чистим Kotlin.
  2. Визначити інтерфейси репозиторіїв та UseCase.
  3. Реалізувати Data-модуль з Retrofit/Room та мапперами.
  4. Налаштувати Hilt для впровадження залежностей.
  5. Підключити Presentation шар через ViewModel.
  6. Написати тести на JVM.

Що входить у налаштування (deliverables)

Компонент Опис
Архітектурна схема Діаграма шарів і залежностей
Конфігурація Hilt Модулі, scope, bindings
Приклад фіча-модуля UseCase + Repository + ViewModel + тести
CI/CD pipeline Інтеграція з GitHub Actions / GitLab CI
Документація README з описом структури
Навчання команди 1-2 сесії з Clean-архітектури

Наші переваги

  • 5+ років досвіду в Android-розробці
  • 20+ успішно впроваджених проєктів Clean Architecture
  • Сертифіковані Google інженери
  • Гарантія якості: покриття тестами не менше 70%
  • Clean Architecture у 2 рази зменшує кількість багів порівняно з монолітом

Терміни та вартість

Налаштування з нуля (одномодульний проєкт): 3–5 днів, вартість від $500. Multi-module з нуля: 1–2 тижні, вартість від $1500. Міграція існуючого моноліту: 3–8 тижнів залежно від обсягу, вартість від $3000. Точну оцінку даємо після аналізу вашого коду.

Зв'яжіться з нами, щоб обговорити ваш проєкт. Ми оцінимо поточну архітектуру і запропонуємо план дій. Замовте консультацію — проведемо перший аудит.

Приклад конфігурації multi-module build.gradle.kts
// build.gradle.kts (root) plugins { id("com.android.application") version "8.1.0" apply false } 

Як Clean Architecture допомагає прискорити розробку?

За рахунок чіткого розділення обов'язків і інверсії залежностей, команди працюють паралельно над різними шарами, що скорочує час випуску релізів.

Наші компетенції:

Останні роботи

  • Розробка мобільного додатка для компанії FEEDME
    Розробка мобільного додатка для компанії FEEDME
    1010
  • Розробка мобільного додатку для компанії XOOMER
    Розробка мобільного додатку для компанії XOOMER
    903
  • Розробка мобільного додатку для компанії RHL
    Розробка мобільного додатку для компанії RHL
    1332
  • Розробка мобільного додатку для компанії ZIPPY
    Розробка мобільного додатку для компанії ZIPPY
    1180
  • Розробка мобільного додатку для компанії Affhome
    Розробка мобільного додатку для компанії Affhome
    1107
  • Розробка мобільного додатку для компанії FLAVORS
    Розробка мобільного додатку для компанії FLAVORS
    714