El problema real
Cuando un proyecto Android empieza a crecer, las dependencias dejan de ser solo librerías externas. También pasan a ser dependencias entre módulos, entre capas y entre decisiones de arquitectura. Y ahí aparece el problema real: si todo depende de todo, el proyecto compila, pero se vuelve difícil de entender, de aislar y de cambiar.
El síntoma típico es este: querés tocar una feature puntual y terminás abriendo cinco módulos, viendo librerías compartidas que no sabías que estaban ahí y peleando con dependencias transitivas que nadie recuerda por qué existen.
Pensar en grafo, no en lista
El error más común es pensar las dependencias como una lista por módulo. En realidad conviene pensarlas como un grafo: quién depende de quién, qué módulos arrastran librerías pesadas, qué contratos se exponen y qué partes del sistema deberían permanecer aisladas.
Ese cambio de mentalidad importa mucho. No alcanza con que cada módulo “tenga lo que necesita”. También importa que el conjunto de relaciones siga siendo entendible y sostenible.
Punto claveUna dependencia no solo agrega funcionalidad: también crea dirección de acoplamiento. Y en proyectos grandes, el acoplamiento termina pesando más que la comodidad inicial.
Qué va en core y qué no
Los módulos core suelen ser necesarios, pero son peligrosos cuando se convierten en un cajón de sastre. Lo que debería vivir ahí es lo que realmente comparten varias features y tiene sentido como infraestructura o contrato común.
- Sí suele ir en core: networking base, UI compartida, modelos comunes, utilidades realmente transversales.
- No debería ir por default: cualquier cosa usada por una sola feature, librerías específicas de negocio o helpers que solo existen para evitar pensar mejor la estructura.
Un módulo compartido mal pensado puede convertirse en el verdadero monolito del proyecto, aunque la app esté dividida en muchos módulos pequeños.
Visibilidad y encapsulamiento
Organizar dependencias no es solo elegir qué librerías usar, sino qué tan visibles las hacés. Si exponés demasiado con api, otros módulos empiezan a depender de detalles que deberían permanecer internos. Si encapsulás bien con implementation, el proyecto mantiene fronteras más sanas.
dependencies {
implementation(project(":core:network"))
implementation(libs.retrofit)
}
La pregunta práctica es: ¿el módulo consumidor realmente debería conocer esta dependencia o este tipo? Si la respuesta es no, conviene ocultarlo.
Qué conviene centralizar
Centralizar ayuda, pero no todo merece centralización del mismo tipo.
- Versiones y aliases: buen caso para Version Catalogs.
- Configuración Gradle repetida: buen caso para convention plugins.
- Reglas de arquitectura: no se resuelven solo con un archivo central; requieren criterio real de módulos y ownership.
Centralizar dependencias sin una estructura clara solo mueve el desorden de lugar. La centralización sirve cuando baja ruido, no cuando esconde problemas.
Módulos compartidos vs features
Una heurística útil es esta: si algo pertenece claramente a una feature, debería vivir lo más cerca posible de esa feature. Solo cuando varias partes del proyecto lo necesitan y tiene una identidad genuinamente compartida, conviene extraerlo.
Eso evita el patrón de “extraer demasiado pronto”, que suele llenar el proyecto de módulos comunes ambiguos y dependencias cruzadas difíciles de justificar.
include(":app")
include(":core:ui")
include(":core:model")
include(":feature:checkout")
include(":feature:profile")
AtenciónUn proyecto con muchos módulos no necesariamente está mejor organizado. Puede estar simplemente más fragmentado.
Errores comunes
- Meter demasiadas librerías en
:appy convertirlo en embudo de todo. - Crear módulos
coredemasiado amplios que terminan siendo un pseudo-monolito. - Usar
apimás de la cuenta y dejar filtrarse detalles internos. - Extraer módulos “por prolijidad” antes de que exista una frontera real.
- Centralizar tanto que el proyecto se vuelva más mágico que entendible.
El patrón detrás de todos esos errores es el mismo: se optimiza la forma antes que la frontera. Pero en dependencias, las fronteras son la forma más importante.
Cierre
Organizar dependencias en un proyecto Android grande no consiste en encontrar la estructura más elegante en abstracto. Consiste en construir un grafo entendible: módulos con responsabilidad clara, dependencias visibles solo cuando deben ser visibles y piezas compartidas solo cuando de verdad son compartidas. Si eso está bien resuelto, Gradle compila mejor, la arquitectura respira mejor y el equipo navega el proyecto con menos fricción.