0Pricing
Android Academy · Урок

Модули функций и ядра

Проводите границы модулей

«Модули функций и ядра» — бесплатный урок Android Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Android Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Android Academy содержит 4 уроков всего.

Два вида модулей

В большинстве модульных приложений Android код организуют в два основных вида модулей: модули feature и core, а сверху добавляют тонкий модуль :app.

  • Модули feature содержат отдельную пользовательскую часть приложения — экран или сценарий.
  • Модули core содержат общую инфраструктуру, используемую многими функциями.

В этом уроке Вы узнаете, как правильно проводить эти границы.

Анатомия модуля функции

Модуль функции, например :feature:profile, содержит всё необходимое одной функции: экраны Compose, её ViewModel и состояние интерфейса. Он устроен вертикально: ему принадлежит весь срез — от интерфейса до модели представления.

Он зависит от модулей core для общих компонентов, но не должен зависеть от других модулей функций.

// feature/profile/ProfileScreen.kt
@Composable
fun ProfileScreen(viewModel: ProfileViewModel = hiltViewModel()) {
    val state by viewModel.uiState.collectAsStateWithLifecycle()
    when (state) {
        is ProfileUiState.Loading -> CircularProgressIndicator()
        is ProfileUiState.Success -> ProfileContent((state as ProfileUiState.Success).user)
        is ProfileUiState.Error -> ErrorMessage()
    }
}

ViewModel функции

У каждой функции есть собственный ViewModel. Он получает данные через репозиторий из модуля core и предоставляет состояние интерфейса. Модулю функции не нужно знать, как загружаются данные, — ему достаточно контракта репозитория.

// feature/profile/ProfileViewModel.kt
@HiltViewModel
class ProfileViewModel @Inject constructor(
    private val userRepository: UserRepository // from :core:data
) : ViewModel() {
    val uiState: StateFlow<ProfileUiState> =
        userRepository.observeUser()
            .map { ProfileUiState.Success(it) }
            .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), ProfileUiState.Loading)
}

Анатомия модуля core

Модуль core устроен горизонтально: он предоставляет одну возможность, используемую разными функциями. Распространённые модули core:

  • :core:model — простые классы данных, общие для всего приложения
  • :core:network — клиенты Retrofit/Ktor
  • :core:database — настройка Room
  • :core:data — репозитории, объединяющие сеть и базу данных
  • :core:designsystem — тема и повторно используемые компонуемые элементы
// core/model/User.kt
data class User(
    val id: String,
    val name: String,
    val avatarUrl: String
)

Модуль системы дизайна

:core:designsystem — один из наиболее часто используемых модулей. Он содержит MaterialTheme, цветовые схемы, типографику и повторно используемые компонуемые элементы, например кнопки и карточки. Каждая функция использует одинаковый внешний вид без копирования кода.

// core/designsystem/AppTheme.kt
@Composable
fun AppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colors = if (darkTheme) DarkColors else LightColors
    MaterialTheme(
        colorScheme = colors,
        typography = AppTypography,
        content = content
    )
}

Модуль данных владеет репозиториями

Модуль :core:data предоставляет интерфейсы репозиториев, от которых зависят функции, скрывая реализацию. Обычно он зависит от :core:network и :core:database, объединяя их в единый источник истины.

// core/data/UserRepository.kt
interface UserRepository {
    fun observeUser(): Flow<User>
    suspend fun refresh()
}

// core/data/OfflineFirstUserRepository.kt
internal class OfflineFirstUserRepository @Inject constructor(
    private val api: UserApi,        // :core:network
    private val dao: UserDao         // :core:database
) : UserRepository {
    override fun observeUser(): Flow<User> = dao.observe().map { it.toUser() }
    override suspend fun refresh() { dao.upsert(api.fetch().toEntity()) }
}

Лёгкие model и designsystem

Модули самого низкого уровня должны зависеть от как можно меньшего числа компонентов. В идеале :core:model вообще не имеет зависимостей от Android — только обычные классы данных Kotlin. Благодаря этому его можно использовать где угодно, а сборка выполняется быстро.

Если бы :core:model начал зависеть от Retrofit или Room, каждый модуль, использующий ваши классы данных, также подтягивал бы эти тяжёлые библиотеки.

// core/model/build.gradle.kts
plugins {
    id("myapp.jvm.library") // pure Kotlin, no Android
}
// No Retrofit, no Room, no Compose here — just data classes

Тонкий модуль :app

Модуль :app — это сборщик. В нём должно быть совсем немного логики: класс Application, единственный MainActivity, корневой NavHost и настройка внедрения зависимостей. Все настоящие экраны находятся в модулях функциональностей.

Тонкий модуль приложения означает, что большинство изменений происходит в функциональностях, поэтому модуль приложения редко пересобирается.

// app/MainActivity.kt
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            AppTheme {            // from :core:designsystem
                AppNavHost()      // routes into :feature:* screens
            }
        }
    }
}

Как провести хорошие границы

Как решить, что должно стать отдельным модулем? Вот несколько практических правил:

  • Функциональность — это экран или сценарий, который пользователь может назвать (Главная, Профиль, Оформление заказа).
  • Модуль ядра — это возможность, повторно используемая в двух и более функциональностях.
  • Если двум функциональностям нужен один и тот же код, переместите его ниже, в модуль ядра.
  • Если модуль делает слишком много несвязанных вещей, разделите его.

Необязательно: разделение API и реализации

В больших приложениях функциональность иногда разделяют на открытый :feature:profile:api (интерфейсы, маршруты навигации) и закрытый :feature:profile:impl (экраны, модели представления). Другие функциональности зависят только от небольшого api, но не от реализации.

Это продвинутый подход; для большинства приложений одного модуля на функциональность вполне достаточно. Просто имейте в виду, что для очень больших кодовых баз существует такой шаблон.

// Other features see only the contract, not the screens
// feature/home depends on :feature:profile:api
interface ProfileEntry {
    val route: String
    fun NavGraphBuilder.register(navController: NavController)
}

Собираем граф

Вот как соединяются уровни в нашем примере. Обратите внимание: зависимости всегда направлены только вниз: app -> feature -> data -> network/database -> model.

// :app           -> :feature:home, :feature:profile
// :feature:home  -> :core:data, :core:designsystem
// :feature:profile -> :core:data, :core:designsystem
// :core:data     -> :core:network, :core:database, :core:model
// :core:network  -> :core:model
// :core:database -> :core:model
// :core:model    -> (nothing)

Быстрая проверка

Модулям :feature:home и :feature:profile нужно получать и кэшировать данные пользователя. Где должен находиться код репозитория?

Итоги: модули функциональностей и ядра

Вы научились разделять код на два уровня:

  • Модули функциональностей — вертикальные срезы (экран + ViewModel + состояние интерфейса).
  • Модули ядра — горизонтальные возможности (model, network, database, data, designsystem).
  • Модуль :app остаётся тонким и только собирает функциональности.
  • Общий код перемещается вниз, в ядро; :core:model не зависит от Android и остаётся лёгким.

Далее вы научитесь управлять зависимостями между этими модулями и поддерживать чистоту графа.

Часто задаваемые вопросы

Урок «Модули функций и ядра» бесплатный?

Да — полный текст урока «Модули функций и ядра» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Android Academy, подпишись на CoddyKit PRO. Курс Android Academy содержит 4 уроков всего.

Чему я научусь в уроке «Модули функций и ядра»?

Проводите границы модулей Ты практикуешь Android Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Android Academy?

Предыдущий опыт не требуется. Android Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Модули функций и ядра»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Android Academy?

Да. Каждый урок Android Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Зачем нужна модульность
  2. Модули функций и ядра
  3. Управление зависимостями модулей
  4. Навигация между модулями
← Назад к Android Academy