Gerenciando dependências entre módulos
Mantenha o grafo limpo e acíclico.
Gerenciando dependências entre módulos é uma aula grátis de Android Academy no CoddyKit. Esta é a aula 3 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.
O grafo de dependências
Cada módulo declara de quais outros módulos depende. Juntos, eles formam um grafo de dependências. Um grafo saudável é um DAG (grafo direcionado acíclico): as dependências apontam em uma única direção e nunca formam um ciclo.
Nesta lição, você aprenderá a declarar dependências de forma organizada, escolher a configuração correta do Gradle, compartilhar versões e evitar ciclos.
Declarando uma dependência de módulo
Você adiciona uma dependência de outro módulo com project(":path:to:module") dentro do bloco dependencies. O caminho reflete as pastas e corresponde ao que foi declarado em settings.gradle.kts.
// feature/profile/build.gradle.kts
dependencies {
implementation(project(":core:data"))
implementation(project(":core:designsystem"))
implementation(project(":core:model"))
}implementation versus api
A configuração escolhida controla o que é exposto aos consumidores:
implementation: a dependência é privada. Os módulos que dependem de você não conseguem vê-la. Esta é a escolha padrão.api: a dependência é reexposta (transitivamente). Use-a apenas quando seus tipos públicos vêm dessa dependência.
Prefira implementation quase sempre — isso melhora a velocidade da compilação, pois alterar uma dependência oculta não obriga os consumidores a serem recompilados.
// core/data/build.gradle.kts
dependencies {
// Repository signatures return :core:model types,
// so consumers need to SEE it -> api
api(project(":core:model"))
// Network is an internal detail -> implementation
implementation(project(":core:network"))
}Por que implementation acelera as compilações
Com implementation, o Gradle sabe que uma alteração em uma dependência oculta não pode afetar a ABI pública do módulo. Portanto, os consumidores não precisam ser recompilados. Com api, uma alteração se propaga por todos os consumidores transitivos.
Regra prática: uma dependência deve estar em api somente se aparecer nos tipos públicos do módulo (tipos de retorno, parâmetros públicos). Caso contrário, use implementation.
// Public -> needs api
fun observeUser(): Flow<User> // Flow and User leak out
// Internal -> implementation is enough
private val client: OkHttpClient // never exposedCentralize as versões: o catálogo de versões
Com muitos módulos, você não vai querer repetir as versões das bibliotecas em todos os lugares. O catálogo de versões do Gradle (gradle/libs.versions.toml) define versões e aliases uma única vez. Cada módulo faz referência ao mesmo alias.
# gradle/libs.versions.toml
[versions]
compose-bom = "2024.09.00"
retrofit = "2.11.0"
[libraries]
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }Usando o catálogo em um módulo
Os módulos fazem referência às bibliotecas por meio do acessador libs gerado. Sem números de versão no arquivo de compilação, as atualizações acontecem em um único lugar.
// core/network/build.gradle.kts
dependencies {
implementation(platform(libs.compose.bom))
implementation(libs.retrofit)
}O pecado capital: ciclos
Um ciclo ocorre quando o módulo A depende de B e B depende novamente de A (diretamente ou por meio de uma cadeia). O Gradle se recusa a compilar uma dependência circular. Isso também indica um problema de design: o limite entre os dois módulos está incorreto.
// :feature:cart -> implementation(project(":feature:checkout"))
// :feature:checkout -> implementation(project(":feature:cart"))
//
// Gradle error:
// Circular dependency between the following tasks:
// :feature:cart:compile -> :feature:checkout:compile -> :feature:cart:compileRompendo um ciclo
Para romper um ciclo, extraia a parte compartilhada para um módulo inferior do qual ambos possam depender. Se duas funcionalidades precisam dos dados uma da outra, esse contrato compartilhado pertence ao núcleo, não a nenhuma das funcionalidades.
Isso restaura o fluxo para baixo: ambas as funcionalidades apontam para o núcleo, e o núcleo nunca aponta de volta para cima.
// Before: cart <-> checkout (cycle)
// After: cart -> :core:order <- checkout
// core/order/OrderContract.kt
data class Order(val items: List<CartItem>, val total: Double)
// feature/cart -> implementation(project(":core:order"))
// feature/checkout-> implementation(project(":core:order"))Inversão: dependa de abstrações
Às vezes, um módulo de baixo nível precisa de um comportamento que reside em um nível mais alto. Em vez de depender de um nível superior, defina uma interface no módulo inferior e permita que o módulo superior forneça a implementação por meio da injeção de dependências. Isso é inversão de dependência.
// core/analytics defines the contract
interface AnalyticsLogger {
fun log(event: String)
}
// :app provides the real implementation and injects it down
@Module
@InstallIn(SingletonComponent::class)
object AnalyticsModule {
@Provides
fun logger(impl: FirebaseAnalyticsLogger): AnalyticsLogger = impl
}Visualize e proteja o grafo
Você pode pedir ao Gradle que imprima ou renderize o grafo de módulos e até adicionar um teste que faça a compilação falhar se uma dependência proibida aparecer (por exemplo, um módulo central dependendo de uma funcionalidade). Ferramentas como o plug-in module-graph geram um diagrama automaticamente.
# Print the project structure
./gradlew projects
# Inspect why :feature:home pulls in a library
./gradlew :feature:home:dependencies --configuration debugRuntimeClasspathUm exemplo limpo e acíclico
Aqui está um grafo saudável. Leia-o de cima para baixo; nenhuma seta aponta de volta para cima e nenhum par de módulos aponta um para o outro. É exatamente isso que você deve buscar.
// :app
// -> :feature:home -> :core:data -> :core:network -> :core:model
// -> :feature:profile -> :core:data -> :core:database -> :core:model
// -> :core:designsystem
//
// Every path ends at :core:model. No cycles. Builds in parallel.Verificação rápida
Seu módulo :core:network usa OkHttpClient apenas internamente — ele nunca aparece na assinatura de nenhuma função pública. Qual configuração do Gradle você deve usar para declarar a dependência do OkHttp?
Recapitulação: gerenciando dependências de módulos
Você aprendeu a manter o grafo de módulos saudável:
- Declare dependências de módulos com
project(":path"). - Use
implementationpor padrão; useapiapenas para tipos que fazem parte da sua superfície pública. - Centralize as versões em um catálogo de versões (
libs.versions.toml). - Nunca crie ciclos — o Gradle os rejeita; rompa-os extraindo o código compartilhado para baixo ou invertendo a dependência com interfaces.
A seguir, você conectará as funcionalidades por meio da navegação sem acoplá-las.
Perguntas Frequentes
A aula “Gerenciando dependências entre módulos” é grátis?
Sim — o texto completo de “Gerenciando dependências entre módulos” é 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 “Gerenciando dependências entre módulos”?
Mantenha o grafo limpo e acíclico. 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 3 de 4.
Quanto tempo leva a aula “Gerenciando dependências entre módulos”?
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
- Por que modularizar
- Módulos de recursos e núcleo
- Gerenciando dependências entre módulos
- Navegação entre módulos