0Pricing
Android Academy · บทเรียน

โมดูลฟีเจอร์และโมดูลแกนกลาง

กำหนดขอบเขตของโมดูล

โมดูลฟีเจอร์และโมดูลแกนกลาง เป็นบทเรียน 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. เหตุใดจึงควรแยกเป็นโมดูล
  2. โมดูลฟีเจอร์และโมดูลแกนกลาง
  3. การจัดการการพึ่งพาระหว่างโมดูล
  4. การนำทางข้ามโมดูล
← กลับไปที่ Android Academy