0Pricing
Android Academy · Aula

Módulos de recursos e núcleo

Defina os limites dos módulos.

Módulos de recursos e núcleo é uma aula grátis de Android Academy no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Android Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Android Academy inclui 4 aulas no total.

Dois tipos de módulos

A maioria dos aplicativos Android modularizados organiza o código em dois tipos principais de módulos: módulos de feature e módulos de core, além de um módulo :app enxuto no topo.

  • Módulos de funcionalidades contêm uma parte do aplicativo voltada ao usuário (uma tela ou um fluxo).
  • Módulos principais contêm a infraestrutura compartilhada usada por várias funcionalidades.

Nesta lição, você aprenderá a definir bem esses limites.

Anatomia de um módulo de funcionalidades

Um módulo de funcionalidades, como :feature:profile, contém tudo de que uma única funcionalidade precisa: suas telas do Compose, seu ViewModel e seu estado da interface. Ele é vertical: é responsável por toda a parte, desde a interface até o view-model.

Ele depende dos módulos principais para obter elementos compartilhados, mas não deve depender de outros módulos de funcionalidades.

// 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()
    }
}

O ViewModel da funcionalidade

Cada funcionalidade é responsável pelo próprio ViewModel. Ele obtém dados por meio de um repositório de um módulo principal e expõe o estado da interface. O módulo de funcionalidades não sabe como os dados são buscados, apenas conhece o contrato do repositório.

// 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)
}

Anatomia de um módulo principal

Um módulo principal é horizontal: ele fornece uma capacidade usada em várias funcionalidades. Entre os módulos principais comuns estão:

  • :core:model — classes de dados simples compartilhadas em todo o aplicativo
  • :core:network — clientes Retrofit/Ktor
  • :core:database — configuração do Room
  • :core:data — repositórios que combinam rede e banco de dados
  • :core:designsystem — tema e componentes reutilizáveis
// core/model/User.kt
data class User(
    val id: String,
    val name: String,
    val avatarUrl: String
)

O módulo do sistema de design

:core:designsystem é um dos módulos mais reutilizados. Ele contém seu MaterialTheme, esquemas de cores, tipografia e componentes reutilizáveis, como botões e cartões. Todas as funcionalidades usam a mesma aparência sem copiar código.

// 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
    )
}

O módulo de dados é responsável pelos repositórios

O módulo :core:data expõe interfaces de repositório das quais as funcionalidades dependem, enquanto oculta a implementação. Normalmente, ele depende de :core:network e :core:database, combinando-os em uma única fonte da verdade.

// 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()) }
}

Mantenha o modelo e o sistema de design leves

Os módulos centrais de nível mais baixo devem depender do mínimo possível. O :core:model idealmente não tem nenhuma dependência do Android — apenas classes de dados simples em Kotlin. Isso permite usá-lo em qualquer lugar e torna a compilação mais rápida.

Se o :core:model começasse a depender do Retrofit ou do Room, todos os módulos que usam suas classes de dados também acabariam incorporando essas bibliotecas pesadas.

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

O módulo :app enxuto

O módulo :app é o montador. Ele deve conter pouquíssima lógica: a classe Application, a única MainActivity, o NavHost de nível superior e a configuração da injeção de dependências. Todas as telas reais ficam nos módulos de funcionalidades.

Um módulo de aplicativo enxuto significa que a maioria das alterações acontece nas funcionalidades, então o módulo :app raramente precisa ser recompilado.

// 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
            }
        }
    }
}

Definindo bons limites

Como decidir o que deve se tornar um módulo? Algumas heurísticas práticas:

  • Uma funcionalidade = uma tela ou um fluxo que o usuário consegue nomear (Início, Perfil, Finalização da compra).
  • Um módulo central = uma capacidade reutilizada por 2 ou mais funcionalidades.
  • Se duas funcionalidades precisam do mesmo código, mova-o para baixo, para um módulo central.
  • Se um módulo faz coisas não relacionadas demais, divida-o.

Opcional: divisão entre api e impl

Às vezes, aplicativos grandes dividem uma funcionalidade em um :feature:profile:api público (interfaces, rotas de navegação) e um :feature:profile:impl privado (telas, modelos de visualização). As outras funcionalidades dependem apenas do pequeno api, nunca da implementação.

Isso é avançado; para a maioria dos aplicativos, um único módulo por funcionalidade é suficiente. Basta saber que esse padrão existe para bases de código muito grandes.

// 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)
}

Montando o grafo

Veja como as camadas se conectam em nosso exemplo. Observe que as dependências sempre apontam para baixo: 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)

Verificação rápida

Os módulos :feature:home e :feature:profile precisam buscar e armazenar em cache os dados do usuário. Onde o código do repositório deve ficar?

Recapitulação: módulos de funcionalidades e centrais

Você aprendeu a dividir o código em duas camadas:

  • Módulos de funcionalidades são fatias verticais (tela + ViewModel + estado da interface).
  • Módulos centrais são capacidades horizontais (modelo, rede, banco de dados, dados, sistema de design).
  • O módulo :app permanece enxuto e apenas monta as funcionalidades.
  • O código compartilhado é movido para baixo, para o núcleo; o :core:model permanece independente do Android e leve.

A seguir, você gerenciará as dependências entre esses módulos e manterá o grafo organizado.

Perguntas Frequentes

A aula “Módulos de recursos e núcleo” é grátis?

Sim — o texto completo de “Módulos de recursos e núcleo” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Android Academy, atualize para CoddyKit PRO. O curso de Android Academy inclui 4 aulas no total.

O que vou aprender em “Módulos de recursos e núcleo”?

Defina os limites dos módulos. Você pratica Android Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Android Academy?

Nenhuma experiência prévia é necessária. Android Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.

Quanto tempo leva a aula “Módulos de recursos e núcleo”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Android Academy?

Sim. Cada aula de Android Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Por que modularizar
  2. Módulos de recursos e núcleo
  3. Gerenciando dependências entre módulos
  4. Navegação entre módulos
← Voltar para Android Academy