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

Clean Architecture Android: позбуваємося спагеті-коду Android-проєкт без чіткої архітектури виглядає передбачувано: `Activity` на 800 рядків, `Retrofit`-інтерфейс викликається прямо з `onClick`, Room DAO повертає `LiveData<List<User>>` напряму у Fragment. Працює до першої вимоги: «додайте кеш», «

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

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

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

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

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

Часті запитання

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

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    895
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    597

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 допомагає прискорити розробку?

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