Pola modularisasi umum

Tetap teratur dengan koleksi Simpan dan kategorikan konten berdasarkan preferensi Anda.

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:

  1. 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.
  2. Mengekspos repositori sebagai API eksternal: API publik modul data harus merupakan repositori karena bertanggung jawab untuk mengekspos data ke seluruh aplikasi.
  3. 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 atau internal Kotlin.
Gambar 1: Sampel modul data dan kontennya

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.

Gambar 2: Setiap tab aplikasi ini dapat ditentukan sebagai 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.

Gambar 3: Contoh modul fitur dan kontennya

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.

Gambar 3: Grafik dependensi modul ragam produk “demo” dan “full”

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.

Gambar 4: Grafik dependensi aplikasi Wear

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.

Gambar 5: Komunikasi dua arah langsung antara modul tidak mungkin dilakukan karena dependensi siklik. Modul mediasi diperlukan untuk mengoordinasikan aliran data antara dua modul independen lainnya

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.

Gambar 6: Dua modul fitur yang mengandalkan 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.