Gestión de dependencias entre módulos
Mantenga el grafo limpio y acíclico
Gestión de dependencias entre módulos es una lección gratuita de Android Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Android Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Android Academy incluye 4 lecciones en total.
El grafo de dependencias
Cada módulo declara de qué otros módulos depende. En conjunto, forman un grafo de dependencias. Un grafo saludable es un DAG (grafo dirigido acíclico): las dependencias apuntan en una sola dirección y nunca forman un bucle.
En esta lección aprenderá a declarar dependencias de forma limpia, elegir la configuración adecuada de Gradle, compartir versiones y evitar ciclos.
Declarar una dependencia de módulo
Puede añadir una dependencia de otro módulo mediante project(":path:to:module") dentro del bloque dependencies. La ruta refleja las carpetas y coincide con lo declarado en settings.gradle.kts.
// feature/profile/build.gradle.kts
dependencies {
implementation(project(":core:data"))
implementation(project(":core:designsystem"))
implementation(project(":core:model"))
}implementation frente a api
La configuración que elija controla qué se filtra a los consumidores:
implementation: la dependencia es privada. Los módulos que dependen de usted no pueden verla. Esta es la opción predeterminada.api: la dependencia se vuelve a exponer (de forma transitiva). Utilícela únicamente cuando sus tipos públicos procedan de esa dependencia.
Prefiera implementation casi siempre: mejora la velocidad de compilación, ya que cambiar una dependencia oculta no obliga a recompilar los consumidores.
// 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 qué implementation acelera las compilaciones
Con implementation, Gradle sabe que un cambio en una dependencia oculta no puede afectar a la ABI pública del módulo. Por eso, los consumidores no necesitan recompilarse. Con api, un cambio se propaga a todos los consumidores transitivos.
Regla general: una dependencia va en api solo si aparece en los tipos públicos del módulo (tipos devueltos o parámetros públicos). En caso contrario, utilice implementation.
// Public -> needs api
fun observeUser(): Flow<User> // Flow and User leak out
// Internal -> implementation is enough
private val client: OkHttpClient // never exposedCentralizar versiones: el catálogo de versiones
Cuando hay muchos módulos, no querrá repetir las versiones de las bibliotecas en todos ellos. El catálogo de versiones de Gradle (gradle/libs.versions.toml) define las versiones y los alias una sola vez. Cada módulo hace referencia al mismo 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" }Usar el catálogo en un módulo
Después, los módulos hacen referencia a las bibliotecas mediante el accessor generado libs. Al no incluir números de versión en el archivo de compilación, las actualizaciones se realizan en un único lugar.
// core/network/build.gradle.kts
dependencies {
implementation(platform(libs.compose.bom))
implementation(libs.retrofit)
}El pecado capital: los ciclos
Existe un ciclo cuando el módulo A depende de B y B vuelve a depender de A (directamente o mediante una cadena). Gradle se niega a compilar una dependencia circular. Además, esto indica un problema de diseño: el límite entre los dos módulos no es el adecuado.
// :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:compileRomper un ciclo
Para romper un ciclo, extraiga la parte compartida a un módulo inferior del que ambos puedan depender. Si dos funcionalidades necesitan los datos de la otra, ese contrato compartido debe estar en core, no en ninguna de las dos funcionalidades.
Así se restablece el flujo descendente: ambas funcionalidades apuntan hacia core, y core nunca apunta de vuelta hacia arriba.
// 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"))Inversión: depender de abstracciones
En ocasiones, un módulo de bajo nivel necesita un comportamiento que reside en un nivel superior. En lugar de depender hacia arriba, defina una interfaz en el módulo inferior y permita que el módulo superior proporcione la implementación mediante la inyección de dependencias. Esto es la inversión de dependencias.
// 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
}Visualizar y proteger el grafo
Puede pedirle a Gradle que imprima o renderice el grafo de módulos, e incluso añadir una prueba que haga fallar la compilación si aparece una dependencia prohibida (por ejemplo, un módulo core que dependa de una funcionalidad). Herramientas como el complemento module-graph generan un diagrama automáticamente.
# Print the project structure
./gradlew projects
# Inspect why :feature:home pulls in a library
./gradlew :feature:home:dependencies --configuration debugRuntimeClasspathUn ejemplo limpio y acíclico
Este es un grafo saludable. Léalo de arriba abajo: ninguna flecha apunta hacia arriba y ningún par de módulos apunta el uno al otro. Esto es exactamente lo que debe conseguir.
// :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.Comprobación rápida
Su módulo :core:network utiliza OkHttpClient únicamente de forma interna: nunca aparece en ninguna firma de función pública. ¿Qué configuración de Gradle debería utilizar para declarar la dependencia de OkHttp?
Resumen: administrar las dependencias de módulos
Ha aprendido a mantener saludable el grafo de módulos:
- Declare las dependencias de módulos con
project(":path"). - Use
implementationde forma predeterminada; utiliceapisolo para los tipos de su superficie pública. - Centralice las versiones en un catálogo de versiones (
libs.versions.toml). - No cree nunca ciclos: Gradle los rechaza; rómpalos extrayendo el código compartido hacia abajo o invirtiendo las dependencias mediante interfaces.
A continuación, conectará las funcionalidades entre sí mediante la navegación, sin acoplarlas.
Preguntas frecuentes
¿La lección «Gestión de dependencias entre módulos» es gratis?
Sí — el texto completo de «Gestión de dependencias entre módulos» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Android Academy, actualiza a CoddyKit PRO. El curso de Android Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Gestión de dependencias entre módulos»?
Mantenga el grafo limpio y acíclico Practicas Android Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Android Academy?
No se requiere experiencia previa. Android Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.
¿Cuánto tiempo toma la lección «Gestión de dependencias entre módulos»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Android Academy?
Sí. Cada lección de Android Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Por qué modularizar
- Módulos de funcionalidades y del núcleo
- Gestión de dependencias entre módulos
- Navegación entre módulos