การทำ Modularization บน Android ปี 2026: สถาปัตยกรรม Multi-Module และคำถามสัมภาษณ์
เชี่ยวชาญสถาปัตยกรรม multi-module ของ Android ด้วย convention plugins, Gradle version catalogs และ feature modules พร้อมคำถามสัมภาษณ์ที่พบบ่อยเกี่ยวกับกลยุทธ์ modularization

การทำ Modularization บน Android เปลี่ยน codebase แบบ monolithic ให้กลายเป็นระบบที่ดูแลรักษาและขยายได้ง่าย ช่วยให้ทีมทำงานแบบขนานโดยไม่เกิดความขัดแย้งของโค้ด แอปพลิเคชัน Android ที่ทำ modularization อย่างดีมีเวลา build ที่เร็วขึ้น ทดสอบแยกส่วนได้ และกระบวนการ onboarding นักพัฒนาใหม่สั้นลง
Modularization แบ่งแอปพลิเคชัน Android ออกเป็น Gradle modules ที่เป็นอิสระและมีขอบเขตชัดเจน Low coupling ระหว่าง modules และ high cohesion ภายในแต่ละ module คือหลักการสำคัญ แอปอ้างอิง Now in Android ของ Google สาธิต pattern นี้ด้วยมากกว่า 40 modules
ทำไมสถาปัตยกรรม Multi-Module ถึงสำคัญสำหรับแอป Android
แอป Android ที่มี module เดียวจะ compile ใหม่ทั้งหมดเมื่อมีการเปลี่ยนแปลงเพียงบรรทัดเดียว การแก้ไข networking layer กระตุ้นการ compile UI ใหม่ นักพัฒนาสองคนที่แก้ไขฟีเจอร์ต่างกันสร้าง merge conflicts ในไฟล์ที่ใช้ร่วมกัน เวลา build แบบ incremental อาจยาวนานกว่า 4 นาที
สถาปัตยกรรม multi-module แก้ไขปัญหาเหล่านี้โดยตรง:
- Build เร็วขึ้น: Gradle compile ใหม่เฉพาะ modules ที่ได้รับผลกระทบ การเปลี่ยนแปลงใน
:feature:checkoutไม่กระทบ:feature:profile - พัฒนาแบบขนาน: ทีมเป็นเจ้าของ modules แยกกันพร้อม API ที่กำหนดไว้ merge conflicts ลดลงอย่างมาก
- Testing แยกส่วน: Unit tests ทำงานบน module เดียวโดยไม่ต้องโหลดแอปทั้งหมด
- นำโค้ดกลับมาใช้: Design system module
:core:uiกลายเป็น library ที่ใช้ข้ามหลายแอปได้
คู่มือ modularization Android อย่างเป็นทางการ แนะนำโครงสร้าง modules ตามฟีเจอร์หรือ layer ที่เปลี่ยนแปลงด้วยกัน ตาม Single Responsibility Principle
ประเภท Module และความรับผิดชอบ
Repository Now in Android กำหนดการจำแนก module ที่พิสูจน์แล้วว่าขยายได้สำหรับแอประดับ enterprise
include(":app")
// Core modules - utilities ที่ใช้ร่วมกัน
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 เฉพาะหน้าจอ
include(":feature:home")
include(":feature:search")
include(":feature:bookmarks")
include(":feature:settings")App module: ประกอบด้วย MainActivity, การตั้งค่า navigation และ scaffolding ระดับแอป ขึ้นกับ feature modules ทั้งหมดและ core modules ที่จำเป็น
Core modules: ให้ฟังก์ชันการทำงานที่ใช้ร่วมกันข้ามฟีเจอร์ :core:network จัดการ API calls :core:database จัดการ Room entities :core:designsystem กำหนด visual components ของแอป
Feature modules: ห่อหุ้มหน้าจอเดียวหรือ user flow แต่ละ feature module ขึ้นกับเฉพาะ core modules ที่ต้องการ ไม่เคยขึ้นกับ feature modules อื่น
โครงสร้างนี้บังคับ dependencies ทิศทางเดียว: features ขึ้นกับ core และ app ขึ้นกับ features
Gradle Version Catalogs สำหรับการจัดการ Dependency
การจัดการ dependencies ข้าม 25+ modules โดยไม่มี version conflicts ต้องการการกำหนดค่าแบบรวมศูนย์ Gradle Version Catalogs แก้ปัญหานี้ด้วยไฟล์ libs.versions.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" }Modules จากนั้นอ้างอิง dependencies ด้วย type-safe accessors:
dependencies {
implementation(libs.bundles.compose)
implementation(libs.hilt.android)
ksp(libs.hilt.compiler)
}Gradle สร้าง accessors เหล่านี้ระหว่าง sync ทำให้ IDE autocompletion และ compile-time validation ทำงานได้
พร้อมที่จะพิชิตการสัมภาษณ์ Android แล้วหรือยังครับ?
ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ
Convention Plugins: การกำหนดค่า Build แบบ DRY
โปรเจกต์ที่มี 25 modules โดยไม่มี convention plugins จะทำซ้ำ compileSdk, minSdk, composeOptions และการใช้ plugin เหมือนกันในทุก build.gradle.kts Modules ต่างกันอาจใช้การกำหนดค่าต่างกันโดยไม่ตั้งใจ
Convention plugins รวมศูนย์ logic นี้ใน included build build-logic
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
}
}
}
}
}ลงทะเบียน plugin ใน build-logic/convention/build.gradle.kts:
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 ตอนนี้ต้องการการกำหนดค่าน้อยที่สุด:
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 จาก Google ใช้ pattern นี้เหมือนกันทุกประการ
รูปแบบการสื่อสารระหว่าง Module
Feature modules ต้องไม่ขึ้นต่อกันโดยตรง ข้อจำกัดนี้รักษา compilation แบบขนานและป้องกัน circular dependencies สาม patterns จัดการการสื่อสาร cross-feature
Navigation ผ่าน Shared Routes
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 จาก :core:navigation โดยไม่รู้ว่า module ไหน implement แต่ละ destination
Shared Domain Models
data class Product(
val id: String,
val name: String,
val price: BigDecimal,
val imageUrl: String
)ทั้ง :feature:catalog และ :feature:checkout ขึ้นกับ :core:model สำหรับ class Product
Event Bus หรือ Shared ViewModel
สำหรับการสื่อสาร runtime, shared event bus ใน :core:common หรือ scoped ViewModel จัดการ events ข้าม feature:
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
}คำถามสัมภาษณ์เกี่ยวกับ Modularization บน Android
Modularization ปรากฏบ่อยในการสัมภาษณ์ Android ระดับ senior ผู้สัมภาษณ์ประเมินความเข้าใจเกี่ยวกับ build systems, การตัดสินใจด้านสถาปัตยกรรม และ trade-offs ในทางปฏิบัติ
เมื่อพูดคุยเกี่ยวกับ modularization ในการสัมภาษณ์ ให้อ้างอิง metrics ที่เป็นรูปธรรม: การปรับปรุงเวลา build, velocity ของทีม หรือจำนวน modules เฉพาะจากโปรเจกต์ คำตอบที่เป็นนามธรรมเกี่ยวกับ "separation of concerns ที่ดีขึ้น" น่าเชื่อถือน้อยกว่า "เวลา build ลดจาก 4 นาทีเหลือ 45 วินาทีหลัง modularization"
Q: ความแตกต่างระหว่าง api และ implementation dependencies ใน Gradle คืออะไร?
implementation dependencies เป็น internal ของ module Consumers ของ module ไม่สามารถเข้าถึงได้ api dependencies ถูก expose แบบ transitive: ถ้า module A ใช้ api(libs.retrofit), module B ที่ขึ้นกับ A สามารถใช้ Retrofit classes ได้โดยตรง
กฎง่ายๆ: ใช้ implementation เป็นค่าเริ่มต้น ใช้ api เฉพาะเมื่อ dependency เป็นส่วนหนึ่งของ public API ของ module การใช้ api มากเกินไปทำลาย encapsulation และทำให้ builds ช้าลง
Q: จะจัดการ navigation ระหว่าง feature modules ที่ไม่ขึ้นต่อกันอย่างไร?
สร้าง module :core:navigation ที่มี route definitions และ navigation interfaces Feature modules ขึ้นกับ shared module นี้ App module เชื่อม destinations กับ implementations นี่ทำตาม dependency inversion principle: features ขึ้นกับ abstractions ไม่ใช่ concrete implementations
Q: Trade-offs ของการมี modules มากเกินไปคืออะไร?
แต่ละ module เพิ่ม overhead การกำหนดค่า Gradle: build files, resource processing, manifest merging เวลา sync เริ่มต้นเพิ่มขึ้น จุดที่เหมาะสมแตกต่างกันตามโปรเจกต์ แต่ 50+ modules เป็นเรื่องปกติในแอปขนาดใหญ่ ประโยชน์ (parallel builds, isolated tests, ownership ที่ชัดเจน) มักจะมากกว่า overhead สำหรับทีม 5+ developers
Q: Convention plugins แตกต่างจาก buildSrc อย่างไร?
การเปลี่ยนแปลง buildSrc กระตุ้น rebuild โปรเจกต์ทั้งหมด Convention plugins ใน included build compile ใหม่เฉพาะเมื่อ code ของ plugin เปลี่ยน ไม่ใช่เมื่อ modules ที่ใช้เปลี่ยน นี่ทำให้ convention plugins เร็วกว่าสำหรับการพัฒนาแบบ iterative นอกจากนี้ convention plugins สามารถ publish เป็น artifact แยกต่างหากเพื่อใช้ซ้ำข้าม repositories
Q: จะ test feature module แบบแยกส่วนได้อย่างไร?
Feature modules ขึ้นกับ core modules ผ่าน interfaces สร้าง fake implementations ใน test fixtures:
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 ผ่าน testing APIs ของ Hilt หรือ manual constructor injection
สำหรับการฝึกคำถามเพิ่มเติมเกี่ยวกับสถาปัตยกรรม Android ดู คำถามสัมภาษณ์สถาปัตยกรรม MVVM และ module dependency injection
เริ่มฝึกซ้อมเลย!
ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ
สรุปสำหรับโปรเจกต์ Android Multi-Module
- โครงสร้าง modules ตามฟีเจอร์ (
:feature:home,:feature:checkout) และ layer (:core:data,:core:ui) Features ขึ้นกับ core ไม่เคยขึ้นต่อกัน - ใช้ Gradle Version Catalogs (
libs.versions.toml) เพื่อรวมศูนย์ dependency versions Bundles รวม dependencies ที่เกี่ยวข้องสำหรับ build files ที่สะอาดขึ้น - Implement convention plugins ใน
build-logicเพื่อกำจัดการกำหนดค่าซ้ำซ้อนข้าม modules การเปลี่ยน plugin ครั้งเดียวอัปเดต modules ที่ใช้ทั้งหมด - บังคับ dependencies ทิศทางเดียว: app ขึ้นกับ features, features ขึ้นกับ core เครื่องมือเช่น Dependency Guard สามารถทำให้ enforcement เป็นอัตโนมัติ
- วัดประสิทธิภาพ build ก่อนและหลัง modularization ติดตามเวลา incremental build ไม่ใช่แค่ clean builds เพื่อยืนยันการปรับปรุง
- รักษาขอบเขต module ให้คงที่ การปรับโครงสร้างบ่อยๆ ลบล้างประโยชน์ของ modularization
คุณหาบั๊กใน Android เจอไหม
โค้ดจริงหนึ่งชิ้น บั๊กที่ซ่อนอยู่หนึ่งจุด วันละหนึ่งครั้ง ลองได้โดยไม่ต้องมีบัญชี

เขียนโดย
Anthony Fillion-Mailletผู้ก่อตั้ง SharpSkill
เป็นนักพัฒนาฟูลสแตกมากว่า 10 ปี ดูแล SharpSkill และรับผิดชอบทุกสิ่งที่เผยแพร่ที่นี่
อัปเดตเมื่อ 17 กันยายน 2569
แชร์
บทความที่เกี่ยวข้อง

Android CameraX ปี 2026: คู่มือถ่ายภาพและวิดีโอพร้อมคำถามสัมภาษณ์
เรียนรู้ CameraX บน Android 2026: การถ่ายภาพ การบันทึกวิดีโอ การผสานรวม Jetpack Compose และคำถามสัมภาษณ์ทางเทคนิคสำหรับนักพัฒนา Android

Jetpack Navigation Compose 2026: การนำทางแบบ Type-Safe และคำถามสัมภาษณ์งาน
เรียนรู้ Jetpack Navigation Compose 2.10 ที่มีการนำทางแบบ type-safe ด้วย Kotlin Serialization คู่มือฉบับสมบูรณ์พร้อมตัวอย่างโค้ดและคำถามสัมภาษณ์ Android

Android WorkManager 2026: คู่มือฉบับสมบูรณ์ Background Tasks, Constraints และคำถามสัมภาษณ์งาน
เรียนรู้ Android WorkManager สำหรับการรัน background tasks พร้อม constraints, chaining และ CoroutineWorker รวมถึงคำถามสัมภาษณ์งานสำหรับ Android developer