Модуляризація Android у 2026 році: Multi-Module архітектура та питання на співбесідах
Посібник з модуляризації Android-додатків у 2026 році. Багатомодульна архітектура, Convention Plugins, Version Catalogs та питання на співбесідах для Kotlin-розробників.

Модуляризація Android перетворює монолітні кодові бази на підтримувані, масштабовані системи, де команди працюють паралельно без втручання в код інших. Добре модуляризований Android-додаток компілюється швидше, тестується ізольовано та дозволяє новим розробникам адаптуватися за дні, а не тижні.
Модуляризація розділяє Android-додаток на незалежні Gradle-модулі з чіткими межами. Низька зв'язність між модулями та висока згуртованість всередині кожного з них — основні принципи. Референсний додаток Google Now in Android демонструє цей патерн з понад 40 модулями.
Чому Multi-Module архітектура важлива для Android-додатків
Одномодульний Android-додаток перекомпільовує все при зміні одного рядка. Модифікація мережевого шару викликає перекомпіляцію UI. Два розробники, що редагують різні функції, створюють merge-конфлікти у спільних файлах. Час компіляції розтягується до 4+ хвилин при інкрементних збірках.
Багатомодульна архітектура безпосередньо вирішує ці проблеми:
- Швидші збірки: Gradle перекомпільовує лише задіяні модулі. Зміна в
:feature:checkoutзалишає:feature:profileнедоторканим. - Паралельна розробка: Команди володіють окремими модулями з визначеними API. Merge-конфлікти значно зменшуються.
- Ізольоване тестування: Юніт-тести працюють на одному модулі без завантаження всього додатку.
- Повторне використання коду: Модуль
:core:uiз дизайн-системою стає бібліотекою, яку можна використовувати в багатьох додатках.
Офіційний посібник з модуляризації Android рекомендує структурувати модулі навколо функцій або шарів, що змінюються разом, відповідно до принципу єдиної відповідальності.
Типи модулів та їх відповідальності
Репозиторій Now in Android встановлює перевірену таксономію модулів, яка масштабується до корпоративних додатків.
include(":app")
// Core modules - shared utilities
include(":core:common")
include(":core:data")
include(":core:database")
include(":core:datastore")
include(":core:designsystem")
include(":core:domain")
include(":core:model")
include(":core:network")
include(":core:ui")
// Feature modules - screen-specific logic
include(":feature:home")
include(":feature:search")
include(":feature:bookmarks")
include(":feature:settings")App модуль: Містить MainActivity, налаштування навігації та каркас на рівні додатку. Залежить від усіх feature-модулів та необхідних core-модулів.
Core модулі: Надають спільну функціональність між функціями. :core:network обробляє API-виклики. :core:database керує Room-сутностями. :core:designsystem визначає візуальні компоненти додатку.
Feature модулі: Інкапсулюють окремий екран або користувацький потік. Кожен feature-модуль залежить лише від тих core-модулів, які йому потрібні, ніколи від інших feature-модулів.
Ця структура забезпечує односпрямовані залежності: функції залежать від core, а app залежить від функцій.
Gradle Version Catalogs для керування залежностями
Керування залежностями в понад 25 модулях без конфліктів версій вимагає централізованої конфігурації. Gradle Version Catalogs вирішують це за допомогою єдиного файлу libs.versions.toml.
# gradle/libs.versions.toml
[versions]
agp = "8.7.0"
kotlin = "2.1.0"
compose-bom = "2026.09.00"
ksp = "2.1.0-1.0.29"
hilt = "2.54"
room = "2.7.0"
retrofit = "2.11.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version = "1.15.0" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" }
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
[bundles]
compose = ["androidx-compose-ui", "androidx-compose-material3"]
room = ["room-runtime", "room-ktx"]
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }Модулі потім посилаються на залежності через type-safe accessor'и:
dependencies {
implementation(libs.bundles.compose)
implementation(libs.hilt.android)
ksp(libs.hilt.compiler)
}Gradle генерує ці accessor'и під час синхронізації, забезпечуючи автодоповнення IDE та валідацію під час компіляції.
Готовий до співбесід з Android?
Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.
Convention Plugins: DRY конфігурація збірки
Проект з 25 модулів без convention plugins повторює ті самі compileSdk, minSdk, composeOptions та застосування плагінів у кожному build.gradle.kts. Різні модулі можуть випадково використовувати різні конфігурації.
Convention plugins централізують цю логіку в included build build-logic.
import com.android.build.api.dsl.LibraryExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
class AndroidLibraryConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
pluginManager.apply("com.android.library")
pluginManager.apply("org.jetbrains.kotlin.android")
extensions.configure<LibraryExtension> {
compileSdk = 35
defaultConfig {
minSdk = 26
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
}
}
}Реєстрація плагіна в build-logic/convention/build.gradle.kts:
plugins {
`kotlin-dsl`
}
dependencies {
compileOnly(libs.android.gradlePlugin)
compileOnly(libs.kotlin.gradlePlugin)
}
gradlePlugin {
plugins {
register("androidLibrary") {
id = "myapp.android.library"
implementationClass = "AndroidLibraryConventionPlugin"
}
register("androidFeature") {
id = "myapp.android.feature"
implementationClass = "AndroidFeatureConventionPlugin"
}
register("androidHilt") {
id = "myapp.android.hilt"
implementationClass = "AndroidHiltConventionPlugin"
}
}
}Feature-модулі тепер вимагають мінімальної конфігурації:
plugins {
alias(libs.plugins.myapp.android.feature)
alias(libs.plugins.myapp.android.hilt)
}
dependencies {
implementation(projects.core.data)
implementation(projects.core.designsystem)
}Now in Android build-logic від Google використовує саме цей патерн.
Патерни комунікації між модулями
Feature-модулі не повинні безпосередньо залежати один від одного. Це обмеження зберігає паралельну компіляцію та запобігає циклічним залежностям. Три патерни забезпечують крос-функціональну комунікацію.
Навігація через спільні маршрути
object Routes {
const val HOME = "home"
const val PRODUCT_DETAIL = "product/{productId}"
const val CHECKOUT = "checkout"
fun productDetail(productId: String) = "product/$productId"
}Feature-модулі імпортують маршрути з :core:navigation без знання про те, який модуль реалізує кожен пункт призначення.
Спільні доменні моделі
data class Product(
val id: String,
val name: String,
val price: BigDecimal,
val imageUrl: String
)Як :feature:catalog, так і :feature:checkout залежать від :core:model для класу Product.
Event Bus або спільний ViewModel
Для комунікації під час виконання спільний event bus в :core:common або scoped ViewModel обробляє події між функціями:
object CartEventBus {
private val _events = MutableSharedFlow<CartEvent>()
val events: SharedFlow<CartEvent> = _events.asSharedFlow()
suspend fun emit(event: CartEvent) {
_events.emit(event)
}
}
sealed interface CartEvent {
data class ItemAdded(val productId: String) : CartEvent
data class ItemRemoved(val productId: String) : CartEvent
}Питання на співбесідах про модуляризацію Android
Модуляризація часто з'являється на співбесідах для senior Android-розробників. Інтерв'юери оцінюють розуміння систем збірки, архітектурних рішень та практичних компромісів.
При обговоренні модуляризації на співбесідах варто посилатися на конкретні метрики: покращення часу компіляції, зростання швидкості команди або конкретну кількість модулів з проектів. Абстрактні відповіді про "кращий розподіл відповідальностей" менш переконливі, ніж "час компіляції зменшився з 4 хвилин до 45 секунд після модуляризації."
П: Яка різниця між залежностями api та implementation в Gradle?
Залежності implementation є внутрішніми для модуля. Споживачі модуля не мають до них доступу. Залежності api експонуються транзитивно: якщо модуль A використовує api(libs.retrofit), модуль B, що залежить від A, може використовувати класи Retrofit напряму.
Правило: за замовчуванням використовувати implementation. Використовувати api лише коли залежність є частиною публічного API модуля. Зловживання api порушує інкапсуляцію та уповільнює збірки.
П: Як обробляти навігацію між feature-модулями, які не залежать один від одного?
Створити модуль :core:navigation, що містить визначення маршрутів та інтерфейси навігації. Feature-модулі залежать від цього спільного модуля. App-модуль з'єднує пункти призначення з реалізаціями. Це дотримується принципу інверсії залежностей: функції залежать від абстракцій, а не від конкретних реалізацій.
П: Які компроміси при занадто великій кількості модулів?
Кожен модуль додає накладні витрати на конфігурацію Gradle: файли збірки, обробка ресурсів, об'єднання маніфестів. Початковий час синхронізації зростає. Критична точка варіюється залежно від проекту, але 50+ модулів є типовим для великих додатків. Переваги (паралельні збірки, ізольовані тести, чітка відповідальність) зазвичай переважають накладні витрати для команд з 5+ розробників.
П: Чим convention plugins відрізняються від buildSrc?
Зміни в buildSrc викликають повну перекомпіляцію проекту. Convention plugins в included build перекомпільовуються лише коли змінюється код плагіна, а не коли змінюються модулі-споживачі. Це робить convention plugins швидшими для ітеративної розробки. Крім того, convention plugins можуть публікуватися як окремий артефакт для повторного використання в різних репозиторіях.
П: Як тестувати feature-модуль ізольовано?
Feature-модулі залежать від core-модулів через інтерфейси. Створювати фейкові реалізації в test fixtures:
class FakeProductRepository : ProductRepository {
private val products = mutableListOf<Product>()
override suspend fun getProducts(): List<Product> = products
fun addProduct(product: Product) {
products.add(product)
}
}Тести feature-модулів ін'єктують фейки через тестові API Hilt або ручну ін'єкцію через конструктор.
Більше практичних питань про архітектуру Android можна знайти в модулі питання на співбесіду про архітектуру MVVM та модуль dependency injection.
Починай практикувати!
Перевір свої знання з нашими симуляторами співбесід та технічними тестами.
Ключові висновки для Multi-Module Android проектів
- Структурувати модулі за функціями (
:feature:home,:feature:checkout) та шарами (:core:data,:core:ui). Функції залежать від core, ніколи одна від одної. - Використовувати Gradle Version Catalogs (
libs.versions.toml) для централізації версій залежностей. Bundles групують пов'язані залежності для чистіших файлів збірки. - Реалізовувати convention plugins в
build-logicдля усунення дубльованої конфігурації між модулями. Одна зміна плагіна оновлює всі модулі-споживачі. - Забезпечувати односпрямовані залежності: app залежить від features, features залежать від core. Інструменти як Dependency Guard можуть автоматизувати контроль.
- Вимірювати продуктивність збірки до і після модуляризації. Відстежувати час інкрементних збірок, а не лише clean builds, щоб підтвердити покращення.
- Зберігати стабільні межі модулів. Часті реструктуризації нівелюють переваги модуляризації.
Чи знайдеш ти помилку в Android?
Справжній фрагмент коду, прихована помилка, одна спроба на день. Щоб спробувати, акаунт не потрібен.

Автор:
Anthony Fillion-MailletЗасновник SharpSkill
Fullstack-розробник понад 10 років. Керує SharpSkill і відповідає за все, що тут публікується.
Оновлено 17 вересня 2026 р.
Поділитися
Пов'язані статті

Android CameraX у 2026: Захоплення Фото та Відео з Питаннями для Співбесід
Посібник з Android CameraX 1.6, що охоплює випадки використання Preview, ImageCapture та VideoCapture. Містить код Kotlin, інтеграцію з Compose та питання для співбесід.

Jetpack Navigation Compose у 2026: Type-Safe Навігація та Питання на Співбесіді
Комплексний посібник з Jetpack Navigation Compose з type-safe навігацією, розширеними патернами та питаннями для співбесід Android-розробників.

Android WorkManager у 2026: Фонові Завдання, Обмеження та Питання на Співбесіду
Повний посібник з WorkManager 2.11 на Kotlin - від базового налаштування через обмеження та ланцюги завдань до тестування та питань на технічних співбесідах.