Modularization Android 2026: Kiến Trúc Multi-Module và Câu Hỏi Phỏng Vấn

Làm chủ kiến trúc multi-module Android với convention plugins, Gradle version catalogs và feature modules. Bao gồm các câu hỏi phỏng vấn phổ biến về chiến lược modularization.

Modularization Android 2026: Kiến Trúc Multi-Module và Câu Hỏi Phỏng Vấn

Modularization Android chuyển đổi codebase nguyên khối thành hệ thống dễ bảo trì và mở rộng, cho phép các team làm việc song song mà không xung đột code. Ứng dụng Android được modularize tốt có thời gian build nhanh hơn, testing độc lập và quy trình onboarding developer mới ngắn hơn.

Tổng Quan Modularization

Modularization chia ứng dụng Android thành các module Gradle độc lập với ranh giới rõ ràng. Coupling thấp giữa các module và cohesion cao trong mỗi module là nguyên tắc chủ đạo. Ứng dụng tham chiếu Now in Android của Google minh họa pattern này với hơn 40 module.

Tại Sao Kiến Trúc Multi-Module Quan Trọng Với Ứng Dụng Android

Ứng dụng Android đơn module sẽ biên dịch lại toàn bộ code khi một dòng thay đổi. Sửa đổi layer networking kích hoạt biên dịch lại UI. Hai developer chỉnh sửa các tính năng khác nhau tạo ra xung đột merge trong các file dùng chung. Thời gian build incremental có thể lên tới hơn 4 phút.

Kiến trúc multi-module giải quyết trực tiếp các vấn đề này:

  • Build nhanh hơn: Gradle chỉ biên dịch lại các module bị ảnh hưởng. Thay đổi trong :feature:checkout không chạm tới :feature:profile.
  • Phát triển song song: Các team sở hữu module riêng với API được định nghĩa. Xung đột merge giảm đáng kể.
  • Testing độc lập: Unit test chạy trên một module duy nhất mà không cần tải toàn bộ ứng dụng.
  • Tái sử dụng code: Module design system :core:ui trở thành library có thể dùng trong nhiều ứng dụng.

Hướng dẫn modularization Android chính thức khuyến nghị cấu trúc module theo tính năng hoặc layer thay đổi cùng nhau, tuân theo Single Responsibility Principle.

Các Loại Module và Trách Nhiệm

Repository Now in Android thiết lập phân loại module đã được chứng minh có thể mở rộng tới ứng dụng enterprise.

settings.gradle.kts - Cấu trúc modulekotlin
include(":app")

// Core modules - tiện ích dùng chung
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 - logic theo màn hình
include(":feature:home")
include(":feature:search")
include(":feature:bookmarks")
include(":feature:settings")

App module: Chứa MainActivity, thiết lập navigation và scaffolding cấp ứng dụng. Phụ thuộc vào tất cả feature modules và core modules cần thiết.

Core modules: Cung cấp chức năng dùng chung giữa các tính năng. :core:network xử lý API calls. :core:database quản lý Room entities. :core:designsystem định nghĩa các thành phần giao diện ứng dụng.

Feature modules: Đóng gói một màn hình hoặc luồng người dùng. Mỗi feature module chỉ phụ thuộc vào các core modules cần thiết, không bao giờ phụ thuộc vào feature modules khác.

Cấu trúc này bắt buộc dependency một chiều: features phụ thuộc vào core, và app phụ thuộc vào features.

Gradle Version Catalogs Cho Quản Lý Dependency

Quản lý dependency trên hơn 25 module mà không có xung đột phiên bản đòi hỏi cấu hình tập trung. Gradle Version Catalogs giải quyết điều này với một file libs.versions.toml duy nhất.

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

Các module sau đó tham chiếu dependency với type-safe accessors:

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

Gradle tạo các accessors này trong quá trình sync, cho phép IDE autocompletion và validation compile-time.

Sẵn sàng chinh phục phỏng vấn Android?

Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.

Convention Plugins: Cấu Hình Build Theo Nguyên Tắc DRY

Dự án với 25 module mà không có convention plugins sẽ lặp lại cùng compileSdk, minSdk, composeOptions và áp dụng plugin trong mỗi build.gradle.kts. Các module khác nhau có thể vô tình sử dụng cấu hình khác nhau.

Convention plugins tập trung logic này trong 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
                }
            }
        }
    }
}

Đăng ký plugin trong 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 giờ chỉ yêu cầu cấu hình tối thiểu:

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 của Google sử dụng chính xác pattern này.

Các Pattern Giao Tiếp Giữa Module

Feature modules không được phụ thuộc trực tiếp vào nhau. Ràng buộc này giữ biên dịch song song và ngăn circular dependencies. Ba pattern xử lý giao tiếp 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 import routes từ :core:navigation mà không biết module nào implement từng destination.

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
)

Cả :feature:catalog:feature:checkout đều phụ thuộc vào :core:model cho class Product.

Event Bus hoặc Shared ViewModel

Để giao tiếp runtime, shared event bus trong :core:common hoặc scoped ViewModel xử lý các 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
}

Câu Hỏi Phỏng Vấn Về Modularization Android

Modularization xuất hiện thường xuyên trong các buổi phỏng vấn Android cấp senior. Người phỏng vấn đánh giá hiểu biết về build systems, quyết định kiến trúc và trade-off thực tế.

Mẹo Phỏng Vấn

Khi thảo luận về modularization trong phỏng vấn, hãy tham chiếu các metric cụ thể: cải thiện thời gian build, velocity của team, hoặc số module cụ thể từ dự án. Câu trả lời trừu tượng về "separation of concerns tốt hơn" ít thuyết phục hơn so với "thời gian build giảm từ 4 phút xuống 45 giây sau khi modularization."

Q: Sự khác biệt giữa dependency api và implementation trong Gradle là gì?

Dependency implementation là nội bộ của module. Consumer của module không thể truy cập chúng. Dependency api được expose một cách transitive: nếu module A sử dụng api(libs.retrofit), module B phụ thuộc vào A có thể sử dụng các class Retrofit trực tiếp.

Quy tắc thực tế: sử dụng implementation mặc định. Chỉ sử dụng api khi dependency là một phần của public API của module. Lạm dụng api phá vỡ encapsulation và làm chậm build.

Q: Làm thế nào để xử lý navigation giữa các feature modules không phụ thuộc vào nhau?

Tạo module :core:navigation chứa định nghĩa route và interface navigation. Feature modules phụ thuộc vào module dùng chung này. App module kết nối destination với implementation. Điều này tuân theo dependency inversion principle: features phụ thuộc vào abstraction, không phải implementation cụ thể.

Q: Trade-off của việc có quá nhiều module là gì?

Mỗi module thêm overhead cấu hình Gradle: build files, resource processing, manifest merging. Thời gian sync ban đầu tăng. Điểm tối ưu thay đổi theo dự án, nhưng 50+ module là phổ biến trong ứng dụng lớn. Lợi ích (parallel builds, isolated tests, ownership rõ ràng) thường lớn hơn overhead cho team 5+ developer.

Q: Convention plugins khác với buildSrc như thế nào?

Thay đổi buildSrc kích hoạt rebuild toàn bộ dự án. Convention plugins trong included build chỉ biên dịch lại khi code plugin thay đổi, không phải khi các module sử dụng thay đổi. Điều này làm convention plugins nhanh hơn cho development lặp đi lặp lại. Ngoài ra, convention plugins có thể được publish như artifact riêng để tái sử dụng trong nhiều repository.

Q: Làm thế nào để test feature module một cách độc lập?

Feature modules phụ thuộc vào core modules thông qua interface. Tạo implementation fake trong 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 inject fakes thông qua testing APIs của Hilt hoặc manual constructor injection.

Để luyện tập thêm các câu hỏi về kiến trúc Android, xem câu hỏi phỏng vấn kiến trúc MVVMmodule dependency injection.

Bắt đầu luyện tập!

Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.

Kết Luận Cho Dự Án Android Multi-Module

  • Cấu trúc module theo tính năng (:feature:home, :feature:checkout) và layer (:core:data, :core:ui). Features phụ thuộc vào core, không bao giờ phụ thuộc vào nhau.
  • Sử dụng Gradle Version Catalogs (libs.versions.toml) để tập trung phiên bản dependency. Bundles nhóm các dependency liên quan cho build files gọn gàng hơn.
  • Implement convention plugins trong build-logic để loại bỏ cấu hình trùng lặp giữa các module. Một thay đổi plugin cập nhật tất cả các module sử dụng.
  • Bắt buộc dependency một chiều: app phụ thuộc vào features, features phụ thuộc vào core. Công cụ như Dependency Guard có thể tự động hóa việc enforcement.
  • Đo lường hiệu suất build trước và sau modularization. Theo dõi thời gian incremental build, không chỉ clean builds, để xác nhận cải thiện.
  • Giữ ranh giới module ổn định. Tái cấu trúc thường xuyên làm mất đi lợi ích của modularization.
Thử thách hôm nay

Bạn có tìm ra lỗi trong Android không?

Một đoạn mã thật, một lỗi ẩn, mỗi ngày một lượt. Không cần tài khoản để thử.

Anthony Fillion-Maillet

Viết bởi

Anthony Fillion-Maillet

Người sáng lập SharpSkill

Lập trình viên fullstack hơn 10 năm. Anh điều hành SharpSkill và chịu trách nhiệm về mọi nội dung đăng tại đây.

Cập nhật ngày 17 tháng 9, 2026

Chia sẻ

Bài viết liên quan