Зачем нужна модульность
Скорость сборки, зоны ответственности и повторное использование
«Зачем нужна модульность» — бесплатный урок Android Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Android Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Android Academy содержит 4 уроков всего.
Проблема монолита
По мере роста приложения Android весь его код часто остаётся в одном модуле app. Это монолит. Сначала это удобно, но со временем начинает мешать: каждое изменение затрагивает один и тот же модуль, сборки становятся медленными, а участники команды постоянно мешают друг другу.
Модульность означает разделение одного большого модуля на множество небольших специализированных модулей Gradle. В этом уроке Вы узнаете, зачем команды это делают и какие конкретные преимущества это даёт.
Что такое модуль на самом деле
В Gradle модуль — это независимо собираемая единица кода со своим файлом build.gradle.kts. В Вашем приложении уже есть как минимум один такой модуль: :app. Каждый модуль объявляется в settings.gradle.kts.
Добавить модуль так же просто, как подключить его. Каждый модуль создаёт собственный результат сборки и может зависеть от других модулей.
// settings.gradle.kts
include(":app")
include(":core:designsystem")
include(":core:data")
include(":feature:home")
include(":feature:profile")Преимущество 1: более быстрые сборки
Самое заметное практическое преимущество — скорость сборки. Gradle может собирать модули параллельно и, что особенно важно, кэшировать и пропускать модули, входные данные которых не изменились.
Если Вы изменили только :feature:profile, Gradle повторно использует уже собранные результаты всех остальных модулей. В монолите любое изменение может потребовать повторной сборки всего приложения.
- Параллельное выполнение для разных модулей
- Инкрементальные сборки: повторно собирается только изменившееся
- Более высокая эффективность удалённого кэша и кэша сборки
// gradle.properties
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configuration-cache=trueПреимущество 2: чёткие границы
Модули устанавливают границы. Код одного модуля видит только то, что другой модуль намеренно предоставляет. Это предотвращает запутанную структуру, в которой каждый класс обращается к любому другому классу.
Видимостью управляют конфигурации Gradle api и implementation. implementation делает зависимость внутренней для модуля, поэтому потребители не смогут случайно использовать её.
// feature/profile/build.gradle.kts
dependencies {
// Exposed to whoever depends on :feature:profile
api(project(":core:model"))
// Private: hidden from consumers of this module
implementation(project(":core:network"))
}Преимущество 3: повторное использование
Если логика находится в специализированном модуле, её можно повторно использовать в любом месте. Модуль :core:designsystem, содержащий тему, цвета и повторно используемые компонуемые элементы, может быть общим для всех функций.
То же относится и к нескольким приложениям: компания может использовать модуль :core:network в нескольких продуктах вместо копирования кода.
// Any feature can pull in shared building blocks
// feature/home/build.gradle.kts
dependencies {
implementation(project(":core:designsystem"))
implementation(project(":core:data"))
}Преимущество 4: принадлежность командам
Модули удобно сопоставлять с зонами ответственности команд. Команда платежей отвечает за :feature:payments, а команда профиля — за :feature:profile. Они могут работать параллельно, сталкиваясь с меньшим числом конфликтов при объединении изменений, поскольку их код находится в разных папках и файлах сборки.
Такие инструменты, как файл CODEOWNERS, могут автоматически запрашивать проверку у нужной команды на основе пути к модулю.
# .github/CODEOWNERS
/feature/payments/ @org/payments-team
/feature/profile/ @org/profile-team
/core/designsystem/ @org/platform-teamПреимущество 5: инкапсуляция с помощью видимости
Внутри модуля модификатор Kotlin internal становится особенно полезным. Класс или функция с модификатором internal видны только внутри собственного модуля. Другие модули буквально не могут обратиться к ним.
Это позволяет предоставлять небольшую общедоступную часть и скрывать детали реализации, чего невозможно строго добиться в одном огромном модуле.
// In :core:data
// Public API other modules may use
fun interface UserRepository {
suspend fun loadUser(id: String): User
}
// Hidden from other modules
internal class DefaultUserRepository(
private val api: UserApi
) : UserRepository {
override suspend fun loadUser(id: String) = api.fetch(id).toUser()
}Цена: дополнительные затраты
Модульность не даётся бесплатно. Для каждого модуля нужно поддерживать отдельный build.gradle.kts, а также решать, какому модулю принадлежит каждый фрагмент кода. Чрезмерное разделение небольшого приложения на модули создаёт лишнюю работу без заметной пользы.
Практическое правило: используйте модули, когда сборка становится слишком долгой, команды мешают друг другу или у Вас есть чёткие повторно используемые уровни. Для небольшого приложения, созданного в свободное время, редко требуется 30 модулей.
Типичная структура модулей
Распространённая масштабируемая структура разделяет модули на уровни app, feature и core. Модуль :app связывает всё воедино, функции содержат экраны для пользователя, а модули core — общую инфраструктуру.
// Conceptual project tree
// app/ <- single entry point, wires features
// feature/
// home/
// profile/
// settings/
// core/
// designsystem/ <- theme + reusable composables
// data/ <- repositories
// network/ <- Retrofit/Ktor
// model/ <- shared data classesПлагины соглашений упрощают структуру
При большом количестве модулей копирование одной и той же настройки Gradle повсюду становится ловушкой. Команды выносят общую конфигурацию в плагины соглашений (в модуль build-logic). После этого каждый настоящий модуль подключает один плагин вместо повторения десятков строк.
Вы увидите этот подход в крупных приложениях с открытым исходным кодом, например в Now in Android. Пока достаточно знать цель: файлы сборки должны оставаться небольшими и единообразными.
// feature/home/build.gradle.kts
plugins {
// One convention plugin sets up Android + Compose + Kotlin
id("myapp.android.feature")
}
android { namespace = "com.myapp.feature.home" }Образ мышления: думайте уровнями
Самая полезная ментальная модель: зависимости должны идти сверху вниз. Функции зависят от core, а core не зависит от функций. Модуль :app находится на самом верхнем уровне и зависит от всего необходимого для сборки приложения.
Если сохранять это направление, граф модулей останется чистым, а циклических зависимостей удастся избежать. Их Вы изучите в одном из следующих уроков.
// Allowed: app -> feature -> core
// Forbidden: core -> feature (upward) or feature -> feature (sideways)
// app/build.gradle.kts
dependencies {
implementation(project(":feature:home"))
implementation(project(":feature:profile"))
}Быстрая проверка
Какое из перечисленных преимуществ является наиболее конкретной повседневной пользой от разделения растущего приложения Android на модули?
Итоги: зачем нужна модульность
Вы узнали, зачем команды разделяют монолит на модули:
- Более быстрые сборки благодаря параллельному выполнению и инкрементальному кэшированию
- Чёткие границы с помощью
api/implementationиinternal - Повторное использование базовых уровней, таких как система дизайна и сеть
- Зоны ответственности команд с меньшим числом конфликтов при объединении изменений
Вы также увидели цену этого подхода — дополнительные файлы сборки — и главное правило: зависимости идут сверху вниз, app -> feature -> core. Далее Вы начнёте проводить фактические границы между модулями функций и core.
Часто задаваемые вопросы
Урок «Зачем нужна модульность» бесплатный?
Да — полный текст урока «Зачем нужна модульность» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Android Academy, подпишись на CoddyKit PRO. Курс Android Academy содержит 4 уроков всего.
Чему я научусь в уроке «Зачем нужна модульность»?
Скорость сборки, зоны ответственности и повторное использование Ты практикуешь Android Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Android Academy?
Предыдущий опыт не требуется. Android Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Зачем нужна модульность»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Android Academy?
Да. Каждый урок Android Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Зачем нужна модульность
- Модули функций и ядра
- Управление зависимостями модулей
- Навигация между модулями