โมดูลฟีเจอร์และโมดูลแกนกลาง
กำหนดขอบเขตของโมดูล
โมดูลฟีเจอร์และโมดูลแกนกลาง เป็นบทเรียน Android Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Android Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Android Academy มีบทเรียนทั้งหมด 4 บทเรียน
โมดูลสองประเภท
แอป Android ที่ทำให้เป็นโมดูลส่วนใหญ่จะจัดโค้ดออกเป็นโมดูลหลักสองประเภท ได้แก่โมดูล feature และโมดูล core พร้อมโมดูล :app ขนาดเล็กที่อยู่ด้านบน
- โมดูล Feature เก็บส่วนหนึ่งของแอปที่ผู้ใช้มองเห็น (หน้าจอหรือขั้นตอนการทำงาน)
- โมดูล Core เก็บโครงสร้างพื้นฐานที่หลายฟีเจอร์ใช้ร่วมกัน
ในบทเรียนนี้ คุณจะเรียนรู้วิธีกำหนดขอบเขตเหล่านี้ให้เหมาะสม
กายวิภาคของโมดูล Feature
โมดูล feature อย่าง :feature:profile จะมีทุกสิ่งที่ฟีเจอร์หนึ่งต้องใช้ ได้แก่หน้าจอ Compose, ViewModel และสถานะ UI โมดูลนี้มีลักษณะเป็น แนวตั้ง คือรับผิดชอบส่วนเต็มตั้งแต่ UI ลงไปจนถึง view-model
โมดูลนี้พึ่งพาโมดูล core สำหรับส่วนที่ใช้ร่วมกัน แต่ไม่ควรพึ่งพาโมดูล feature อื่น
// feature/profile/ProfileScreen.kt
@Composable
fun ProfileScreen(viewModel: ProfileViewModel = hiltViewModel()) {
val state by viewModel.uiState.collectAsStateWithLifecycle()
when (state) {
is ProfileUiState.Loading -> CircularProgressIndicator()
is ProfileUiState.Success -> ProfileContent((state as ProfileUiState.Success).user)
is ProfileUiState.Error -> ErrorMessage()
}
}ViewModel ของ Feature
แต่ละฟีเจอร์รับผิดชอบ ViewModel ของตัวเอง โดยดึงข้อมูลผ่าน repository จากโมดูล core และเปิดเผยสถานะ UI โมดูล feature ไม่จำเป็นต้องรู้ว่าดึงข้อมูลมาอย่างไร รู้เพียงสัญญาของ repository เท่านั้น
// feature/profile/ProfileViewModel.kt
@HiltViewModel
class ProfileViewModel @Inject constructor(
private val userRepository: UserRepository // from :core:data
) : ViewModel() {
val uiState: StateFlow<ProfileUiState> =
userRepository.observeUser()
.map { ProfileUiState.Success(it) }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), ProfileUiState.Loading)
}กายวิภาคของโมดูล Core
โมดูล core มีลักษณะเป็น แนวนอน โดยจัดเตรียมความสามารถหนึ่งอย่างที่หลายฟีเจอร์ใช้ร่วมกัน โมดูล core ที่พบได้ทั่วไป ได้แก่:
:core:model— คลาสข้อมูลธรรมดาที่ใช้ร่วมกันทุกส่วน:core:network— ไคลเอ็นต์ Retrofit/Ktor:core:database— การตั้งค่า Room:core:data— repository ที่รวมเครือข่ายกับฐานข้อมูล:core:designsystem— ธีมและ composable ที่นำกลับมาใช้ใหม่ได้
// core/model/User.kt
data class User(
val id: String,
val name: String,
val avatarUrl: String
)โมดูลระบบการออกแบบ
:core:designsystem เป็นหนึ่งในโมดูลที่ถูกนำกลับมาใช้มากที่สุด โดยเก็บ MaterialTheme, ชุดสี, รูปแบบตัวอักษร และ composable ที่นำกลับมาใช้ใหม่ได้ เช่น ปุ่มและการ์ด ทุกฟีเจอร์จึงใช้รูปลักษณ์เดียวกันโดยไม่ต้องคัดลอกโค้ด
// core/designsystem/AppTheme.kt
@Composable
fun AppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colors = if (darkTheme) DarkColors else LightColors
MaterialTheme(
colorScheme = colors,
typography = AppTypography,
content = content
)
}โมดูลข้อมูลรับผิดชอบ Repository
โมดูล :core:data เปิดเผย ส่วนติดต่อของ repository ที่ฟีเจอร์พึ่งพา ขณะเดียวกันก็ซ่อนรายละเอียดการทำงานภายใน โดยปกติจะพึ่งพา :core:network และ :core:database แล้วรวมทั้งสองส่วนให้เป็นแหล่งข้อมูลเดียวที่เชื่อถือได้
// core/data/UserRepository.kt
interface UserRepository {
fun observeUser(): Flow<User>
suspend fun refresh()
}
// core/data/OfflineFirstUserRepository.kt
internal class OfflineFirstUserRepository @Inject constructor(
private val api: UserApi, // :core:network
private val dao: UserDao // :core:database
) : UserRepository {
override fun observeUser(): Flow<User> = dao.observe().map { it.toUser() }
override suspend fun refresh() { dao.upsert(api.fetch().toEntity()) }
}รักษา model และระบบออกแบบให้มีน้ำหนักเบา
โมดูลแกนกลางระดับล่างสุดควรขึ้นต่อกับสิ่งอื่นให้น้อยที่สุดเท่าที่ทำได้ โดยอุดมคติแล้ว :core:model ไม่ควรมีการขึ้นต่อกับ Android เลย — มีเพียงคลาสข้อมูล Kotlin ธรรมดาเท่านั้น วิธีนี้ทำให้ใช้งานได้ทุกที่และสร้างได้รวดเร็ว
หาก :core:model เริ่มขึ้นต่อกับ Retrofit หรือ Room ทุกโมดูลที่ใช้คลาสข้อมูลของคุณก็จะต้องดึงไลบรารีขนาดใหญ่เหล่านั้นเข้ามาด้วย
// core/model/build.gradle.kts
plugins {
id("myapp.jvm.library") // pure Kotlin, no Android
}
// No Retrofit, no Room, no Compose here — just data classesโมดูล :app แบบบาง
โมดูล :app ทำหน้าที่เป็น ตัวประกอบ โมดูลนี้ควรมีตรรกะน้อยมาก ประกอบด้วยคลาส Application, MainActivity เพียงหนึ่งตัว, NavHost ระดับบนสุด และการเชื่อมต่อระบบฉีดการขึ้นต่อ ฟีเจอร์และหน้าจอจริงทั้งหมดควรอยู่ในโมดูลฟีเจอร์
เมื่อโมดูลแอปมีขนาดบาง การเปลี่ยนแปลงส่วนใหญ่จะเกิดขึ้นในฟีเจอร์ ทำให้โมดูลแอปแทบไม่ต้องสร้างใหม่
// app/MainActivity.kt
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppTheme { // from :core:designsystem
AppNavHost() // routes into :feature:* screens
}
}
}
}กำหนดขอบเขตที่ดี
คุณจะตัดสินใจอย่างไรว่าอะไรควรกลายเป็นโมดูล ลองใช้หลักเกณฑ์เชิงปฏิบัติเหล่านี้:
- ฟีเจอร์ = หน้าจอหรือกระบวนการที่ผู้ใช้สามารถเรียกชื่อได้ (หน้าหลัก โปรไฟล์ การชำระเงิน)
- โมดูล แกนกลาง = ความสามารถที่ฟีเจอร์ตั้งแต่ 2 ฟีเจอร์ขึ้นไปนำกลับมาใช้ซ้ำ
- หากฟีเจอร์สองฟีเจอร์ต้องใช้โค้ดเดียวกัน ให้ย้ายโค้ดนั้น ลงไป อยู่ในโมดูลแกนกลาง
- หากโมดูลหนึ่งทำงานที่ไม่เกี่ยวข้องกันมากเกินไป ให้แยกโมดูลนั้นออก
ตัวเลือกเสริม: การแยก api กับ impl
บางครั้งแอปขนาดใหญ่จะแยกฟีเจอร์หนึ่งออกเป็น :feature:profile:api สาธารณะ (ส่วนติดต่อและเส้นทางการนำทาง) กับ :feature:profile:impl ส่วนตัว (หน้าจอและ ViewModel) ฟีเจอร์อื่นจะขึ้นต่อเฉพาะ api ขนาดเล็กเท่านั้น ไม่ขึ้นต่อกับส่วนการนำไปใช้งาน
นี่เป็นแนวทางขั้นสูง สำหรับแอปส่วนใหญ่ การมีหนึ่งโมดูลต่อฟีเจอร์ก็เพียงพอแล้ว เพียงทราบไว้ว่ามีรูปแบบนี้สำหรับโค้ดเบสขนาดใหญ่มาก
// Other features see only the contract, not the screens
// feature/home depends on :feature:profile:api
interface ProfileEntry {
val route: String
fun NavGraphBuilder.register(navController: NavController)
}ประกอบกราฟเข้าด้วยกัน
นี่คือวิธีเชื่อมชั้นต่าง ๆ เข้าด้วยกันสำหรับตัวอย่างของเรา โปรดสังเกตว่าการขึ้นต่อจะชี้ลงเท่านั้น: app -> feature -> data -> network/database -> model
// :app -> :feature:home, :feature:profile
// :feature:home -> :core:data, :core:designsystem
// :feature:profile -> :core:data, :core:designsystem
// :core:data -> :core:network, :core:database, :core:model
// :core:network -> :core:model
// :core:database -> :core:model
// :core:model -> (nothing)ตรวจสอบอย่างรวดเร็ว
โมดูล :feature:home และ :feature:profile ของคุณต้องดึงและแคชข้อมูลผู้ใช้ทั้งคู่ โค้ดของคลังข้อมูลควรอยู่ที่ใด
สรุป: โมดูลฟีเจอร์และแกนกลาง
คุณได้เรียนรู้วิธีแยกโค้ดออกเป็นสองชั้น:
- โมดูลฟีเจอร์ คือส่วนย่อยในแนวตั้ง (หน้าจอ + ViewModel + สถานะส่วนติดต่อผู้ใช้)
- โมดูลแกนกลาง คือความสามารถในแนวนอน (โมเดล เครือข่าย ฐานข้อมูล ข้อมูล ระบบออกแบบ)
- โมดูล
:appจะคงความบางไว้และทำหน้าที่ประกอบฟีเจอร์ต่าง ๆ เท่านั้น - โค้ดที่ใช้ร่วมกันจะย้าย ลงไป ในแกนกลาง ส่วน
:core:modelจะไม่ขึ้นต่อกับ Android และมีน้ำหนักเบา
ถัดไป คุณจะจัดการการขึ้นต่อระหว่างโมดูลเหล่านี้และรักษากราฟให้สะอาด
คำถามที่พบบ่อย
บทเรียน “โมดูลฟีเจอร์และโมดูลแกนกลาง” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “โมดูลฟีเจอร์และโมดูลแกนกลาง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Android Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Android Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “โมดูลฟีเจอร์และโมดูลแกนกลาง”
กำหนดขอบเขตของโมดูล คุณปฏิบัติ Android Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Android Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Android Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “โมดูลฟีเจอร์และโมดูลแกนกลาง” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Android Academy นี้ได้ไหม
ได้ บทเรียน Android Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- เหตุใดจึงควรแยกเป็นโมดูล
- โมดูลฟีเจอร์และโมดูลแกนกลาง
- การจัดการการพึ่งพาระหว่างโมดูล
- การนำทางข้ามโมดูล