Modularización Android en 2026: Arquitectura Multi-Módulo y Preguntas de Entrevista

Guía completa sobre modularización Android en 2026. Aprende a estructurar una arquitectura multi-módulo, usar catálogos de versiones Gradle y dominar las preguntas de entrevista técnica esenciales.

Arquitectura multi-módulo Android con módulos feature y core

La modularización Android transforma bases de código monolíticas en sistemas mantenibles y escalables donde los equipos trabajan en paralelo sin conflictos de código. Una aplicación Android bien modularizada se compila más rápido, se prueba de forma aislada y permite la incorporación de nuevos desarrolladores en días en lugar de semanas.

Modularización en Resumen

La modularización divide una aplicación Android en módulos Gradle independientes con límites claros. El bajo acoplamiento entre módulos y la alta cohesión dentro de cada módulo son los principios rectores. La aplicación de referencia Now in Android de Google demuestra este patrón con más de 40 módulos.

Por Qué la Arquitectura Multi-Módulo Es Importante para Aplicaciones Android

Una aplicación Android de módulo único recompila todo cuando cambia una sola línea. Una modificación en la capa de red dispara la recompilación de la interfaz de usuario. Dos desarrolladores editando diferentes funcionalidades crean conflictos de merge en archivos compartidos. Los tiempos de compilación se extienden a más de 4 minutos en builds incrementales.

La arquitectura multi-módulo aborda directamente estos puntos de dolor:

  • Compilaciones más rápidas: Gradle solo recompila los módulos afectados. Un cambio en :feature:checkout deja :feature:profile intacto.
  • Desarrollo paralelo: Los equipos poseen módulos separados con APIs definidas. Los conflictos de merge disminuyen significativamente.
  • Pruebas aisladas: Las pruebas unitarias se ejecutan contra un solo módulo sin cargar toda la aplicación.
  • Reutilización de código: Un módulo de sistema de diseño :core:ui se convierte en una biblioteca utilizable en múltiples aplicaciones.

La guía oficial de modularización Android recomienda estructurar módulos alrededor de funcionalidades o capas que cambian juntas, siguiendo el Principio de Responsabilidad Única.

Tipos de Módulos y Sus Responsabilidades

El repositorio Now in Android establece una taxonomía de módulos probada que escala a aplicaciones empresariales.

settings.gradle.kts - Estructura de móduloskotlin
include(":app")

// Módulos core - utilidades compartidas
include(":core:common")
include(":core:data")
include(":core:database")
include(":core:datastore")
include(":core:designsystem")
include(":core:domain")
include(":core:model")
include(":core:network")
include(":core:ui")

// Módulos feature - lógica específica de pantallas
include(":feature:home")
include(":feature:search")
include(":feature:bookmarks")
include(":feature:settings")

Módulo app: Contiene MainActivity, configuración de navegación y scaffolding a nivel de aplicación. Depende de todos los módulos feature y los módulos core requeridos.

Módulos core: Proporcionan funcionalidad compartida entre features. :core:network maneja llamadas API. :core:database gestiona entidades Room. :core:designsystem define los componentes visuales de la aplicación.

Módulos feature: Encapsulan una única pantalla o flujo de usuario. Cada módulo feature depende solo de los módulos core que necesita, nunca de otros módulos feature.

Esta estructura impone dependencias unidireccionales: las features dependen de core, y app depende de features.

Catálogos de Versiones Gradle para Gestión de Dependencias

Gestionar dependencias en más de 25 módulos sin conflictos de versiones requiere configuración centralizada. Los Catálogos de Versiones Gradle resuelven esto con un único archivo libs.versions.toml.

toml
# gradle/libs.versions.toml
[versions]
agp = "8.7.0"
kotlin = "2.1.0"
compose-bom = "2026.09.00"
ksp = "2.1.0-1.0.29"
hilt = "2.54"
room = "2.7.0"
retrofit = "2.11.0"

[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version = "1.15.0" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" }
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }

[bundles]
compose = ["androidx-compose-ui", "androidx-compose-material3"]
room = ["room-runtime", "room-ktx"]

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }

Los módulos luego referencian dependencias con accesores tipados:

feature/home/build.gradle.ktskotlin
dependencies {
    implementation(libs.bundles.compose)
    implementation(libs.hilt.android)
    ksp(libs.hilt.compiler)
}

Gradle genera estos accesores durante la sincronización, habilitando autocompletado del IDE y validación en tiempo de compilación.

¿Listo para aprobar tus entrevistas de Android?

Practica con nuestros simuladores interactivos, flashcards y tests técnicos.

Convention Plugins: Configuración de Build DRY

Un proyecto de 25 módulos sin convention plugins repite los mismos compileSdk, minSdk, composeOptions y aplicaciones de plugins en cada build.gradle.kts. Diferentes módulos pueden accidentalmente usar configuraciones diferentes.

Los convention plugins centralizan esta lógica en un build incluido build-logic.

build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.ktkotlin
import com.android.build.api.dsl.LibraryExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure

class AndroidLibraryConventionPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        with(target) {
            pluginManager.apply("com.android.library")
            pluginManager.apply("org.jetbrains.kotlin.android")

            extensions.configure<LibraryExtension> {
                compileSdk = 35
                defaultConfig {
                    minSdk = 26
                    testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
                }
                compileOptions {
                    sourceCompatibility = JavaVersion.VERSION_17
                    targetCompatibility = JavaVersion.VERSION_17
                }
            }
        }
    }
}

Registro del plugin en build-logic/convention/build.gradle.kts:

build-logic/convention/build.gradle.ktskotlin
plugins {
    `kotlin-dsl`
}

dependencies {
    compileOnly(libs.android.gradlePlugin)
    compileOnly(libs.kotlin.gradlePlugin)
}

gradlePlugin {
    plugins {
        register("androidLibrary") {
            id = "myapp.android.library"
            implementationClass = "AndroidLibraryConventionPlugin"
        }
        register("androidFeature") {
            id = "myapp.android.feature"
            implementationClass = "AndroidFeatureConventionPlugin"
        }
        register("androidHilt") {
            id = "myapp.android.hilt"
            implementationClass = "AndroidHiltConventionPlugin"
        }
    }
}

Los módulos feature ahora requieren configuración mínima:

feature/home/build.gradle.ktskotlin
plugins {
    alias(libs.plugins.myapp.android.feature)
    alias(libs.plugins.myapp.android.hilt)
}

dependencies {
    implementation(projects.core.data)
    implementation(projects.core.designsystem)
}

El build-logic de Now in Android de Google utiliza exactamente este patrón.

Patrones de Comunicación Entre Módulos

Los módulos feature no deben depender directamente unos de otros. Esta restricción preserva la compilación paralela y previene dependencias circulares. Tres patrones manejan la comunicación entre features.

core/navigation/src/main/kotlin/Routes.ktkotlin
object Routes {
    const val HOME = "home"
    const val PRODUCT_DETAIL = "product/{productId}"
    const val CHECKOUT = "checkout"
    
    fun productDetail(productId: String) = "product/$productId"
}

Los módulos feature importan rutas desde :core:navigation sin saber qué módulo implementa cada destino.

Modelos de Dominio Compartidos

core/model/src/main/kotlin/Product.ktkotlin
data class Product(
    val id: String,
    val name: String,
    val price: BigDecimal,
    val imageUrl: String
)

Tanto :feature:catalog como :feature:checkout dependen de :core:model para la clase Product.

Event Bus o ViewModel Compartido

Para comunicación en tiempo de ejecución, un event bus compartido en :core:common o un ViewModel con scope maneja eventos entre features:

core/common/src/main/kotlin/CartEventBus.ktkotlin
object CartEventBus {
    private val _events = MutableSharedFlow<CartEvent>()
    val events: SharedFlow<CartEvent> = _events.asSharedFlow()
    
    suspend fun emit(event: CartEvent) {
        _events.emit(event)
    }
}

sealed interface CartEvent {
    data class ItemAdded(val productId: String) : CartEvent
    data class ItemRemoved(val productId: String) : CartEvent
}

Preguntas de Entrevista sobre Modularización Android

La modularización aparece frecuentemente en entrevistas Android de nivel senior. Los entrevistadores evalúan la comprensión de sistemas de build, decisiones de arquitectura y compensaciones prácticas.

Consejo de Entrevista

Al discutir modularización en entrevistas, se recomienda referenciar métricas concretas: mejoras en tiempos de build, ganancias de velocidad del equipo o números específicos de módulos de proyectos reales. Las respuestas abstractas sobre "mejor separación de responsabilidades" son menos convincentes que "los tiempos de build bajaron de 4 minutos a 45 segundos después de la modularización".

P: ¿Cuál es la diferencia entre dependencias api e implementation en Gradle?

Las dependencias implementation son internas al módulo. Los consumidores del módulo no pueden acceder a ellas. Las dependencias api se exponen transitivamente: si el módulo A usa api(libs.retrofit), el módulo B que depende de A puede usar clases de Retrofit directamente.

Regla general: usar implementation por defecto. Solo usar api cuando la dependencia es parte de la API pública del módulo. El uso excesivo de api rompe la encapsulación y ralentiza los builds.

P: ¿Cómo manejar la navegación entre módulos feature que no dependen entre sí?

Se debe crear un módulo :core:navigation conteniendo definiciones de rutas e interfaces de navegación. Los módulos feature dependen de este módulo compartido. El módulo app conecta los destinos con las implementaciones. Esto sigue el principio de inversión de dependencias: las features dependen de abstracciones, no de implementaciones concretas.

P: ¿Cuáles son las compensaciones de tener demasiados módulos?

Cada módulo agrega overhead de configuración Gradle: archivos de build, procesamiento de recursos, fusión de manifests. Los tiempos de sincronización inicial aumentan. El punto de inflexión varía por proyecto, pero 50+ módulos es común en aplicaciones grandes. Los beneficios (builds paralelos, pruebas aisladas, propiedad clara) típicamente superan el overhead para equipos de 5+ desarrolladores.

P: ¿En qué se diferencian los convention plugins de buildSrc?

Los cambios en buildSrc disparan una reconstrucción completa del proyecto. Los convention plugins en un build incluido solo recompilan cuando cambia el código del plugin, no cuando cambian los módulos consumidores. Esto hace que los convention plugins sean más rápidos para desarrollo iterativo. Además, los convention plugins pueden publicarse como un artefacto separado para reutilización entre repositorios.

P: ¿Cómo probar un módulo feature de forma aislada?

Los módulos feature dependen de módulos core a través de interfaces. Se deben crear implementaciones falsas en test fixtures:

core/data/src/testFixtures/kotlin/FakeProductRepository.ktkotlin
class FakeProductRepository : ProductRepository {
    private val products = mutableListOf<Product>()
    
    override suspend fun getProducts(): List<Product> = products
    
    fun addProduct(product: Product) {
        products.add(product)
    }
}

Las pruebas de módulos feature inyectan fakes vía las APIs de testing de Hilt o inyección manual por constructor.

Para más preguntas de práctica sobre arquitectura Android, consultar las preguntas de entrevista sobre arquitectura MVVM y el módulo sobre inyección de dependencias.

¡Empieza a practicar!

Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.

Puntos Clave para Proyectos Android Multi-Módulo

  • Estructurar módulos por feature (:feature:home, :feature:checkout) y por capa (:core:data, :core:ui). Las features dependen de core, nunca entre sí.
  • Usar Catálogos de Versiones Gradle (libs.versions.toml) para centralizar versiones de dependencias. Los bundles agrupan dependencias relacionadas para archivos de build más limpios.
  • Implementar convention plugins en build-logic para eliminar configuración duplicada entre módulos. Un cambio en el plugin actualiza todos los módulos consumidores.
  • Imponer dependencias unidireccionales: app depende de features, features dependen de core. Herramientas como Dependency Guard pueden automatizar esta aplicación.
  • Medir el rendimiento de build antes y después de la modularización. Seguir los tiempos de build incrementales, no solo los builds limpios, para validar mejoras.
  • Mantener los límites de módulos estables. Las reestructuraciones frecuentes anulan los beneficios de la modularización.
Reto diario

¿Sabrías detectar el bug en Android?

Un fragmento real, un bug oculto, un intento al día. Sin cuenta para probar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador de SharpSkill

Desarrollador fullstack desde hace más de 10 años. Dirige SharpSkill y responde por todo lo que se publica aquí.

Actualizado el 17 de septiembre de 2026

Etiquetas

#android
#modularization
#architecture
#kotlin
#gradle

Compartir

Artículos relacionados