Modularisasi Android 2026: Arsitektur Multi-Modul dan Pertanyaan Interview

Kuasai arsitektur multi-modul Android dengan convention plugins, Gradle version catalogs, dan feature modules. Termasuk pertanyaan interview umum tentang strategi modularisasi.

Modularisasi Android 2026: Arsitektur Multi-Modul dan Pertanyaan Interview

Modularisasi Android mengubah codebase monolitik menjadi sistem yang dapat dipelihara dan diskalakan, memungkinkan tim bekerja secara paralel tanpa konflik kode. Aplikasi Android yang termodularisasi dengan baik memiliki waktu build lebih cepat, pengujian terisolasi, dan proses onboarding developer baru yang lebih singkat.

Sekilas Tentang Modularisasi

Modularisasi membagi aplikasi Android menjadi modul Gradle independen dengan batasan yang jelas. Coupling rendah antar modul dan kohesi tinggi dalam setiap modul adalah prinsip utamanya. Aplikasi referensi Now in Android dari Google mendemonstrasikan pola ini dengan 40+ modul.

Mengapa Arsitektur Multi-Modul Penting untuk Aplikasi Android

Aplikasi Android dengan satu modul akan mengkompilasi ulang seluruh kode ketika satu baris berubah. Modifikasi pada layer networking memicu rekompilasi UI. Dua developer yang mengedit fitur berbeda menciptakan konflik merge pada file bersama. Waktu build incremental bisa mencapai 4+ menit.

Arsitektur multi-modul mengatasi masalah ini secara langsung:

  • Build lebih cepat: Gradle hanya mengkompilasi ulang modul yang terpengaruh. Perubahan di :feature:checkout tidak menyentuh :feature:profile.
  • Development paralel: Tim memiliki modul terpisah dengan API yang terdefinisi. Konflik merge berkurang signifikan.
  • Testing terisolasi: Unit test berjalan pada satu modul tanpa memuat seluruh aplikasi.
  • Code reuse: Modul design system :core:ui menjadi library yang dapat digunakan di berbagai aplikasi.

Panduan modularisasi Android resmi merekomendasikan struktur modul berdasarkan fitur atau layer yang berubah bersama, mengikuti Single Responsibility Principle.

Tipe Modul dan Tanggung Jawabnya

Repository Now in Android menetapkan taksonomi modul yang terbukti dapat diskalakan ke aplikasi enterprise.

settings.gradle.kts - Struktur modulkotlin
include(":app")

// Core modules - utilitas bersama
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")

// Feature modules - logika spesifik layar
include(":feature:home")
include(":feature:search")
include(":feature:bookmarks")
include(":feature:settings")

App module: Berisi MainActivity, setup navigasi, dan scaffolding level aplikasi. Bergantung pada semua feature modules dan core modules yang diperlukan.

Core modules: Menyediakan fungsionalitas bersama antar fitur. :core:network menangani API calls. :core:database mengelola Room entities. :core:designsystem mendefinisikan komponen visual aplikasi.

Feature modules: Mengenkapsulasi satu layar atau alur pengguna. Setiap feature module hanya bergantung pada core modules yang dibutuhkan, tidak pernah pada feature modules lain.

Struktur ini memaksakan dependensi searah: features bergantung pada core, dan app bergantung pada features.

Gradle Version Catalogs untuk Manajemen Dependensi

Mengelola dependensi di 25+ modul tanpa konflik versi memerlukan konfigurasi terpusat. Gradle Version Catalogs menyelesaikan ini dengan satu file 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" }

Modul kemudian mereferensikan dependensi dengan type-safe accessors:

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

Gradle menghasilkan accessors ini selama sync, memungkinkan IDE autocompletion dan validasi compile-time.

Siap menguasai wawancara Android Anda?

Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.

Convention Plugins: Konfigurasi Build yang DRY

Proyek dengan 25 modul tanpa convention plugins akan mengulang compileSdk, minSdk, composeOptions, dan aplikasi plugin yang sama di setiap build.gradle.kts. Modul yang berbeda dapat secara tidak sengaja menggunakan konfigurasi yang berbeda.

Convention plugins memusatkan logika ini dalam included build 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
                }
            }
        }
    }
}

Daftarkan plugin di 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"
        }
    }
}

Feature modules sekarang hanya memerlukan konfigurasi minimal:

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

Build-logic Now in Android dari Google menggunakan pola yang sama persis.

Pola Komunikasi Antar Modul

Feature modules tidak boleh bergantung satu sama lain secara langsung. Batasan ini menjaga kompilasi paralel dan mencegah circular dependencies. Tiga pola menangani komunikasi cross-feature.

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

Feature modules mengimpor routes dari :core:navigation tanpa mengetahui modul mana yang mengimplementasikan setiap destinasi.

Shared Domain Models

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

Baik :feature:catalog maupun :feature:checkout bergantung pada :core:model untuk class Product.

Event Bus atau Shared ViewModel

Untuk komunikasi runtime, shared event bus di :core:common atau scoped ViewModel menangani event cross-feature:

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
}

Pertanyaan Interview Modularisasi Android

Modularisasi sering muncul dalam interview Android level senior. Interviewer menilai pemahaman tentang build systems, keputusan arsitektur, dan trade-off praktis.

Tips Interview

Saat membahas modularisasi dalam interview, referensikan metrik konkret: peningkatan waktu build, velocity tim, atau jumlah modul spesifik dari proyek. Jawaban abstrak tentang "separation of concerns yang lebih baik" kurang meyakinkan dibanding "waktu build turun dari 4 menit ke 45 detik setelah modularisasi."

Q: Apa perbedaan antara dependensi api dan implementation di Gradle?

Dependensi implementation bersifat internal untuk modul. Consumer modul tidak dapat mengaksesnya. Dependensi api diekspos secara transitif: jika modul A menggunakan api(libs.retrofit), modul B yang bergantung pada A dapat menggunakan class Retrofit secara langsung.

Aturan praktis: gunakan implementation secara default. Hanya gunakan api ketika dependensi merupakan bagian dari public API modul. Penggunaan api berlebihan merusak enkapsulasi dan memperlambat build.

Q: Bagaimana cara menangani navigasi antar feature modules yang tidak saling bergantung?

Buat modul :core:navigation yang berisi definisi route dan interface navigasi. Feature modules bergantung pada modul bersama ini. App module menghubungkan destinasi dengan implementasinya. Ini mengikuti dependency inversion principle: features bergantung pada abstraksi, bukan implementasi konkret.

Q: Apa trade-off dari memiliki terlalu banyak modul?

Setiap modul menambah overhead konfigurasi Gradle: build files, resource processing, manifest merging. Waktu sync awal meningkat. Titik optimal bervariasi per proyek, tetapi 50+ modul umum di aplikasi besar. Manfaatnya (parallel builds, isolated tests, ownership yang jelas) biasanya lebih besar dari overhead untuk tim 5+ developer.

Q: Bagaimana convention plugins berbeda dari buildSrc?

Perubahan buildSrc memicu rebuild seluruh proyek. Convention plugins dalam included build hanya mengkompilasi ulang ketika kode plugin berubah, bukan ketika modul yang mengonsumsi berubah. Ini membuat convention plugins lebih cepat untuk development iteratif. Selain itu, convention plugins dapat dipublikasikan sebagai artifact terpisah untuk digunakan kembali di berbagai repository.

Q: Bagaimana cara menguji feature module secara terisolasi?

Feature modules bergantung pada core modules melalui interface. Buat implementasi fake di 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)
    }
}

Test feature module menginjeksi fakes melalui testing APIs Hilt atau manual constructor injection.

Untuk latihan pertanyaan lebih lanjut tentang arsitektur Android, lihat pertanyaan interview arsitektur MVVM dan modul dependency injection.

Mulai berlatih!

Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.

Kesimpulan untuk Proyek Android Multi-Modul

  • Struktur modul berdasarkan fitur (:feature:home, :feature:checkout) dan layer (:core:data, :core:ui). Features bergantung pada core, tidak pernah pada satu sama lain.
  • Gunakan Gradle Version Catalogs (libs.versions.toml) untuk memusatkan versi dependensi. Bundles mengelompokkan dependensi terkait untuk build files yang lebih bersih.
  • Implementasikan convention plugins di build-logic untuk menghilangkan konfigurasi duplikat antar modul. Satu perubahan plugin memperbarui semua modul yang mengonsumsi.
  • Terapkan dependensi searah: app bergantung pada features, features bergantung pada core. Tools seperti Dependency Guard dapat mengotomatisasi enforcement.
  • Ukur performa build sebelum dan sesudah modularisasi. Lacak waktu incremental build, bukan hanya clean builds, untuk memvalidasi peningkatan.
  • Jaga batasan modul tetap stabil. Restrukturisasi yang sering meniadakan manfaat modularisasi.
Tantangan harian

Bisakah kamu menemukan bug di Android?

Satu potongan kode nyata, satu bug tersembunyi, satu percobaan per hari. Tanpa akun untuk mencoba.

Anthony Fillion-Maillet

Ditulis oleh

Anthony Fillion-Maillet

Pendiri SharpSkill

Developer fullstack selama lebih dari 10 tahun. Ia menjalankan SharpSkill dan bertanggung jawab atas semua yang diterbitkan di sini.

Diperbarui 17 September 2026

Bagikan

Artikel terkait