Tidak ada satu strategi modularisasi yang cocok untuk semua project. Karena sifat Gradle yang fleksibel, tidak banyak batasan dalam mengatur project. Halaman ini memberikan ringkasan tentang beberapa aturan umum dan pola lazim yang dapat Anda gunakan saat mengembangkan aplikasi Android multi-modul.
Prinsip kohesi tinggi dan pengaitan rendah
Salah satu cara untuk menunjukkan karakter codebase modular adalah dengan menggunakan properti pengaitan dan kohesi. Pengaitan mengukur sejauh mana modul saling bergantung satu sama lain. Kohesi, dalam konteks ini, mengukur bagaimana elemen modul tunggal terkait secara fungsional. Sebagai aturan umum, Anda harus berusaha mendapatkan pengaitan rendah dan kohesi tinggi:
- Pengaitan rendah berarti modul harus sebisa mungkin terpisah dari satu sama lain, sehingga perubahan pada satu modul hanya sedikit atau sama sekali tidak berdampak pada modul lainnya. Modul seharusnya tidak memiliki pengetahuan tentang cara kerja internal modul lainnya.
- Kohesi tinggi berarti modul harus terdiri dari kumpulan kode yang bertindak sebagai sistem. Modul tersebut harus memiliki tanggung jawab yang jelas dan tetap berada dalam batas pengetahuan domain tertentu. Pertimbangkan contoh aplikasi eBook. Mungkin tidak tepat untuk menggabungkan kode terkait buku dan pembayaran dalam modul yang sama karena keduanya adalah dua domain fungsional yang berbeda.
Jenis modul
Cara Anda mengelola modul sebagian besar bergantung pada arsitektur aplikasi Anda. Berikut beberapa jenis modul umum yang dapat Anda perkenalkan di aplikasi sambil mengikuti arsitektur aplikasi yang direkomendasikan.
Modul data
Modul data biasanya berisi repositori, sumber data, dan class model. Tiga tanggung jawab utama modul data adalah:
- Mengenkapsulasi semua logika data dan bisnis dari domain tertentu: Setiap modul data harus bertanggung jawab untuk menangani data yang mewakili domain tertentu. Modul data dapat menangani berbagai jenis data selama masih berkaitan.
- Mengekspos repositori sebagai API eksternal: API publik modul data harus merupakan repositori karena bertanggung jawab untuk mengekspos data ke seluruh aplikasi.
- Menyembunyikan semua detail implementasi dan sumber data dari luar:
Sumber data hanya boleh diakses oleh repositori dari modul yang sama.
File tetap tersembunyi dari luar. Anda dapat menerapkannya dengan menggunakan kata kunci visibilitas
private
atauinternal
Kotlin.

Modul fitur
Fitur adalah bagian terisolasi dari fungsi aplikasi yang biasanya sesuai dengan layar atau serangkaian layar yang terkait erat, seperti alur pendaftaran atau checkout. Jika aplikasi Anda memiliki navigasi panel bawah, kemungkinan setiap tujuan adalah fitur.

Fitur dikaitkan dengan layar atau tujuan di aplikasi Anda. Oleh karena itu,
fitur kemungkinan memiliki UI terkait dan ViewModel
untuk menangani logika
dan status. Satu fitur tidak harus dibatasi pada satu tampilan atau
tujuan navigasi. Modul fitur bergantung pada modul data.

Modul aplikasi
Modul aplikasi adalah titik entri ke aplikasi. Modul tersebut bergantung pada modul fitur dan biasanya menyediakan navigasi root. Modul aplikasi tunggal dapat dikompilasi ke sejumlah biner yang berbeda berkat varian build.

Jika aplikasi Anda menargetkan beberapa jenis perangkat, seperti Automotive, Wear, atau TV, Anda dapat mempertimbangkan untuk menentukan modul aplikasi untuk setiap jenis perangkat. Hal ini membantu memisahkan dependensi khusus platform.

Modul umum
Modul umum, yang juga dikenal sebagai modul inti, berisi kode yang sering digunakan modul lain. Modul ini mengurangi redundansi dan tidak merepresentasikan lapisan tertentu dalam arsitektur aplikasi. Berikut adalah contoh modul umum:
- Modul UI: Jika menggunakan elemen UI kustom atau branding yang rumit di aplikasi, sebaiknya Anda mempertimbangkan untuk merangkum koleksi widget ke dalam modul untuk semua fitur yang akan digunakan kembali. Hal ini dapat membantu menjadikan UI Anda konsisten di berbagai fitur. Misalnya, jika tema Anda terpusat, Anda dapat menghindari pemfaktoran ulang yang memusingkan saat terjadi rebranding.
- Modul analisis: Pelacakan sering kali ditentukan oleh persyaratan bisnis dengan tidak terlalu mempertimbangkan arsitektur software. Pelacak analisis sering digunakan di banyak komponen yang tidak terkait. Jika itu yang terjadi, sebaiknya Anda memiliki modul analisis khusus.
- Modul jaringan: Jika banyak modul memerlukan koneksi jaringan, Anda dapat mempertimbangkan memiliki modul khusus untuk menyediakan klien http. Hal ini sangat berguna terutama saat klien Anda memerlukan konfigurasi kustom.
- Modul utilitas: Utilitas, yang juga dikenal sebagai helper, biasanya berupa kode kecil yang digunakan kembali di seluruh aplikasi. Contoh utilitas meliputi helper pengujian, fungsi pemformatan mata uang, validator email, atau operator kustom.
Komunikasi modul ke modul
Modul jarang ada dalam pemisahan total dan sering kali mengandalkan modul lain dan berkomunikasi dengannya. Penting untuk menjaga agar pengaitan tetap rendah bahkan ketika modul bekerja sama dan sering bertukar informasi. Terkadang, komunikasi langsung antara dua modul tidak diinginkan seperti dalam kasus batasan arsitektur. Hal ini mungkin juga tidak dapat dilakukan, seperti dalam kasus dependensi siklik.

Untuk mengatasi masalah ini, Anda dapat membuat modul ketiga yang melakukan mediasi antara dua modul lainnya. Modul mediator dapat memproses pesan dari kedua modul dan meneruskannya sesuai kebutuhan. Dalam aplikasi contoh kami, layar checkout perlu mengetahui buku mana yang akan dibeli meskipun peristiwa tersebut berasal dari layar terpisah yang merupakan bagian dari fitur yang berbeda. Dalam hal ini, mediator adalah modul yang memiliki grafik navigasi (biasanya modul aplikasi). Pada contoh, kita menggunakan navigasi untuk meneruskan data dari fitur beranda ke fitur checkout menggunakan komponen Navigation.
navController.navigate("checkout/$bookId")
Tujuan checkout menerima ID buku sebagai argumen yang digunakannya untuk
mengambil informasi tentang buku. Anda dapat menggunakan handle status tersimpan untuk
mengambil argumen navigasi di dalam ViewModel
fitur tujuan.
class CheckoutViewModel(savedStateHandle: SavedStateHandle, …) : ViewModel() {
val uiState: StateFlow<CheckoutUiState> =
savedStateHandle.getStateFlow<String>("bookId", "").map { bookId ->
// produce UI state calling bookRepository.getBook(bookId)
}
…
}
Anda tidak boleh meneruskan objek sebagai argumen navigasi. Sebagai gantinya, gunakan ID sederhana yang dapat digunakan fitur untuk mengakses dan memuat resource yang diinginkan dari lapisan data. Dengan cara ini, Anda dapat menjaga agar pengaitan tetap rendah dan tidak melanggar prinsip satu sumber kebenaran.
Dalam contoh di bawah, kedua modul fitur bergantung pada modul data yang sama. Hal ini memungkinkan untuk meminimalkan jumlah data yang diperlukan modul mediator untuk diteruskan dan menjaga pengaitan di antara modul tetap rendah. Daripada meneruskan objek, modul seharusnya bertukar ID dasar dan memuat resource dari modul data bersama.

Praktik terbaik umum
Seperti yang disebutkan di awal, tidak ada cara yang tepat untuk mengembangkan aplikasi multi-modul. Sama seperti banyak arsitektur software, ada banyak cara untuk memodularisasi aplikasi. Meskipun demikian, rekomendasi umum berikut dapat membantu menjadikan kode Anda lebih mudah dibaca, mudah dikelola, dan dapat diuji.
Pastikan konfigurasi Anda konsisten
Setiap modul memunculkan overhead konfigurasi. Jika jumlah modul Anda mencapai batas tertentu, mengelola konfigurasi yang konsisten menjadi tantangan. Misalnya, modul harus menggunakan dependensi versi yang sama. Jika Anda perlu mengupdate sejumlah besar modul hanya untuk menambahkan versi dependensi, hal ini tidak hanya membutuhkan upaya, tetapi juga ruang untuk potensi kesalahan. Untuk mengatasi masalah ini, Anda dapat menggunakan salah satu alat gradle untuk memusatkan konfigurasi:
- Katalog versi adalah daftar dependensi jenis yang aman yang dihasilkan oleh Gradle selama sinkronisasi. Ini adalah tempat utama untuk mendeklarasikan semua dependensi Anda dan tersedia untuk semua modul dalam project.
- Gunakan plugin konvensi untuk membagikan logika build antar-modul.
Ekspos sesedikit mungkin
Antarmuka publik modul harus bersifat minimal dan hanya menampilkan
hal-hal penting. Detail implementasi apa pun tidak boleh bocor ke luar. Buat cakupan semuanya
sekecil mungkin. Gunakan cakupan visibilitas private
atau internal
Kotlin untuk membuat deklarasi bersifat pribadi. Saat mendeklarasikan
dependensi dalam modul, pilih implementation
daripada api
. Yang kedua
mengekspos dependensi transitif kepada konsumen modul Anda. Menggunakan
implementasi dapat meningkatkan waktu build karena dapat mengurangi jumlah modul
yang perlu dibuat ulang.
Memilih modul Kotlin & Java
Ada tiga jenis modul penting yang didukung Android Studio:
- Modul aplikasi adalah titik entri ke aplikasi Anda. Aset dapat berisi
kode sumber, resource, aset, dan
AndroidManifest.xml
. Output modul aplikasi adalah Android App Bundle (AAB) atau Paket Aplikasi Android (APK). - Modul library memiliki konten yang sama dengan modul aplikasi. Class ini digunakan oleh modul Android lain sebagai dependensi. Output modul library adalah Android Archive (AAR) yang secara struktural identik dengan modul aplikasi, tetapi dikompilasi menjadi file Android Archive (AAR) yang nantinya dapat digunakan oleh modul lain sebagai dependensi. Modul library memungkinkan enkapsulasi dan penggunaan kembali logika dan resource yang sama di banyak modul aplikasi.
- Library Kotlin dan Java tidak berisi resource, aset, atau file manifes Android.
Karena modul Android memiliki overhead, sebaiknya Anda menggunakan Kotlin atau Java sebanyak mungkin.