อธิบายว่า Feature-Sliced Design คืออะไร — วิธีการทางสถาปัตยกรรมแบบโมดูลาร์ของฟรอนต์เอนด์ที่ที่ตั้งอยู่บนการแบ่งโปรเจกต์ตามฟีเจอร์ทางธุรกิจแทนเลเยอร์ทางเทคนิค แตกต่างจากสถาปัตยกรรมแบบเลเยอร์คลาสสิก (คอนโทรลเลอร์, เซอร์วิส, รีโพซิทอรี) FSD จัดกลุ่มโค้ดตามความสามารถเชิงฟังก์ชันของแอปพลิเคชัน: แต่ละฟีเจอร์ประกอบด้วยลอจิก, UI และข้อมูลของตัวเอง ตามผลสำรวจ State of Frontend 2024 นักพัฒนา React 23% ใช้ FSD เป็นวิธีการทางสถาปัตยกรรมหลัก ทำให้เป็นวิธีที่ได้รับความนิยมเป็นอันดับสองรองจากโครงสร้าง Feature-based แบบบริสุทธิ์
ประเด็นสำคัญ
Feature-Sliced Design (FSD) คือวิธีการทางสถาปัตยกรรมแอปพลิเคชันฟรอนต์เอนด์ที่ถูกเสนอครั้งแรกในปี 2021 โดยชุมชน feature-sliced.design แนวคิดหลักของ FSD คือการจัดกลุ่มโค้ดตามฟีเจอร์ทางธุรกิจ (สไลด์) ซึ่งแต่ละชิ้นเป็นหน่วยที่พึ่งพาตนเองได้: ประกอบด้วยลอจิกทางธุรกิจ, อินเทอร์เฟซผู้ใช้, การโต้ตอบกับ API, โมเดลข้อมูล และการทดสอบของตัวเอง สิ่งนี้ทำให้ FSD แตกต่างจากสถาปัตยกรรมแบบเลเยอร์คลาสสิกที่โค้ดถูกแบ่งตามเกณฑ์ทางเทคนิค (controller, service, repository)
วิธีการนี้ยืมแนวคิดจาก Domain-Driven Design (DDD) และ Bounded Context: แต่ละฟีเจอร์ของแอปพลิเคชันคือ bounded context ที่แยกจากกันพร้อมขอบเขตที่ชัดเจน การเปลี่ยนแปลงภายในฟีเจอร์หนึ่งไม่ควรทำให้ฟีเจอร์อื่นเสียหายหากใช้เฉพาะ API สาธารณะของสไลด์เท่านั้น ตามผลสำรวจ State of Frontend 2024 FSD อยู่ในอันดับสองในด้านความนิยมในหมู่สถาปัตยกรรม React (23%) รองจากโครงสร้าง Feature-based ที่ไม่เป็นทางการ (31%)
ในการพัฒนามือถือ FSD ปรับใช้ตามลักษณะเฉพาะของโมดูล Android และเฟรมเวิร์ก iOS ที่ IT Sectr เราใช้ FSD สำหรับโปรเจกต์ที่มีหน้าจอ 10+ จอและ 3+ ทีม — วิธีการนี้ช่วยให้พัฒนาฟีเจอร์ได้อย่างอิสระและลดความขัดแย้งใน git ลง 40% เมื่อเทียบกับโมโนรีโปที่ไม่มีขอบเขตสไลด์
FSD กำหนดเจ็ดเลเยอร์แบบลำดับชั้น แต่ละเลเยอร์ประกอบด้วยโค้ดในระดับนามธรรมที่แน่นอน กฎทางสถาปัตยกรรมหลักคือ เลเยอร์สามารถนำเข้าโค้ดจากเลเยอร์ที่อยู่ด้านล่างเท่านั้น การละเมิดกฎนี้ (การนำเข้าเลเยอร์ features ใน entities) ถือเป็นข้อผิดพลาดทางสถาปัตยกรรมและถูกบล็อกโดย linter
| เลเยอร์ | วัตถุประสงค์ | นำเข้า |
|---|---|---|
| app | การเริ่มต้นแอปพลิเคชัน, โพรไวเดอร์, สไตล์ส่วนกลาง, การกำหนดเส้นทาง | เลเยอร์ใดก็ได้ |
| processes | กระบวนการทางธุรกิจที่รวมหลายฟีเจอร์ (ออนบอร์ดิง, การชำระเงิน) | pages, features, entities, shared |
| pages | การประกอบฟีเจอร์บนหน้า, การกำหนดเส้นทางหน้า | features, entities, shared |
| features | สถานการณ์ผู้ใช้: ฟอร์มเข้าสู่ระบบ, รายการโปรด, ตัวกรองค้นหา | entities, shared |
| entities | เอนทิตีทางธุรกิจ: User, Product, Order, Cart | shared |
| widgets | คอมโพเนนต์ UI แบบประกอบ: Header, Sidebar, ArticleCard | shared, entities |
| shared | ยูทิลิตี, UI-kit, ไคลเอนต์ API, การกำหนดค่า — อิสระจากลอจิกทางธุรกิจ | เฉพาะไลบรารีภายนอก |
ตัวอย่างโครงสร้างไดเรกทอรีของโปรเจกต์ FSD:
src/
├── app/ // เลเยอร์แอปพลิเคชัน
│ ├── providers/
│ ├── router/
│ └── styles/
├── pages/ // หน้า — การประกอบฟีเจอร์
│ └── main/
├── features/ // ฟีเจอร์ — สถานการณ์ผู้ใช้
│ ├── auth/ // สไลด์ «การรับรองความถูกต้อง»
│ │ ├── ui/
│ │ ├── model/
│ │ └── api/
│ └── productList/ // สไลด์ «รายการสินค้า»
│ ├── ui/
│ └── model/
├── entities/ // เอนทิตีทางธุรกิจ
│ ├── user/
│ └── product/
├── widgets/ // คอมโพเนนต์แบบประกอบ
│ └── header/
└── shared/ // ยูทิลิตีที่ใช้ร่วมกันและ UI-kit
└── ui/กฎ «เลเยอร์มองลงไปด้านล่างเท่านั้น» เป็นรากฐานที่สำคัญของ FSD ถ้า feature auth นำเข้า entity user — ถูกต้อง ถ้า entity user เริ่มนำเข้า feature auth — นั่นเป็นการพึ่งพาแบบวนซ้ำและการละเมิดการแยกตัว เพื่อให้แน่ใจว่ากฎนี้มีผลใช้ ปลั๊กอิน ESLint (eslint-plugin-fsd) หรือ linter ที่กำหนดเองของ API สาธารณะของสไลด์จะถูกนำมาใช้
สไลด์ (Slice) — หน่วยหลักของการจัดกลุ่มใน FSD ที่สอดคล้องกับหนึ่งฟีเจอร์หรือเอนทิตีทางธุรกิจ แต่ละสไลด์อยู่ภายในหนึ่งในเจ็ดเลเยอร์ (features, entities, widgets, pages) และประกอบด้วยชุดโค้ดที่สมบูรณ์สำหรับการใช้งานฟังก์ชันเฉพาะ: คอมโพเนนต์ UI, โมเดลข้อมูล, ไคลเอนต์ API, ค่าคงที่และการทดสอบ
ขอบเขตของสไลด์ถูกกำหนดโดยโดเมนธุรกิจ: feature auth รวมทุกอย่างที่เกี่ยวข้องกับการอนุญาต (ฟอร์มเข้าสู่ระบบ, ฟอร์มลงทะเบียน, รีเซ็ตรหัสผ่าน); entity user รวมโมเดล User, UserRepository และการทำให้เป็นอนุกรม ขอบเขตไม่ควรทับซ้อนกัน: ถ้า feature auth ต้องการข้อมูลผู้ใช้ — มันจะนำเข้า entity user แทนที่จะคัดลอกลอจิก ในการพัฒนามือถือ สไลด์ FSD มักสอดคล้องกับโมดูล Gradle ใน Android หรือแพ็กเกจ Swift ใน iOS
สไลด์ถูกแยกตัวอย่างเข้มงวด: โครงสร้างภายในของสไลด์หนึ่งไม่สามารถมองเห็นได้โดยสไลด์อื่น สำหรับการโต้ตอบระหว่างสไลด์ จะใช้ API สาธารณะ — ไฟล์ index.ts/index.js ที่ส่งออกเฉพาะสิ่งที่อนุญาตให้ใช้ภายนอกเท่านั้น ทุกสิ่งอื่นเป็นโมดูลส่วนตัว แนวทางนี้ป้องกันการพึ่งพาโดยไม่ได้ตั้งใจและทำให้การรีแฟกเตอร์ง่ายขึ้น: การเปลี่ยนการใช้งานส่วนตัวของสไลด์หนึ่งไม่ส่งผลกระทบต่อสไลด์อื่น
ภายในแต่ละสไลด์ FSD โค้ดจะถูกจัดระเบียบเพิ่มเติมตาม เซกเมนต์ — หมวดหมู่ทางเทคนิคที่ซ้ำกันในทุกสไลด์ ชุดเซกเมนต์มาตรฐานประกอบด้วย ui (คอมโพเนนต์อินเทอร์เฟซ), model (ลอจิกทางธุรกิจ, Store, Actions, Reducer), api (คำขอไปยังเซิร์ฟเวอร์, มิวเทชัน), lib (ยูทิลิตีและตัวช่วย) และ config (การกำหนดค่าฟีเจอร์)
| เซกเมนต์ | เนื้อหา | ตัวอย่าง |
|---|---|---|
| ui/ | คอมโพเนนต์ React/Vue/SwiftUI, สไตล์, Storybook | LoginForm.tsx, login.module.css |
| model/ | Store, Reducer, Actions, ชนิด, สัญญา | LoginStore.ts, authReducer.ts |
| api/ | ไคลเอนต์ HTTP, มิวเทชัน, การเรียก RPC | authApi.ts, loginMutation.ts |
| lib/ | ฟังก์ชันตัวช่วย, ตัวตรวจสอบความถูกต้อง | validateEmail.ts, formatPhone.ts |
| config/ | ค่าคงที่, การกำหนดค่าฟีเจอร์ | authConfig.ts, endpoints.ts |
เซกเมนต์เป็นคำแนะนำ ไม่ใช่กฎที่เข้มงวด ถ้าสไลด์เล็ก สามารถรวมเซกเมนต์ได้ สำหรับสไลด์ขนาดใหญ่ (ฟีเจอร์ที่มี 10+ ไฟล์) การแบ่งเซกเมนต์เป็นสิ่งจำเป็น — หากไม่มีมัน โครงสร้างภายในจะกลายเป็น «ตะกร้า» ที่มี 50 ไฟล์อย่างรวดเร็ว ซึ่งการค้นหาคอมโพเนนต์ที่ต้องการใช้เวลาหลายนาที ในการพัฒนามือถือ เซกเมนต์มักถูกแทนที่ด้วยโครงสร้างไฟล์ตามประเภท: แต่ละฟีเจอร์คือไฟล์ Swift แยกต่างหากหรือคลาส Kotlin ที่มีชนิดภายใน
ในการพัฒนามือถือ FSD ปรับใช้ตามคุณสมบัติเฉพาะของแพลตฟอร์ม — โครงสร้างแบบโมดูลาร์ของ Android (โมดูล Gradle) และ Swift Package Manager การปรับใช้ Android ถือว่าแต่ละสไลด์เป็นโมดูล Gradle แยกต่างหากพร้อม build.gradle ของตัวเอง โมดูล feature-auth, feature-profile, entity-user, shared-ui ถูกแยกจากกันในระดับบิลด์: feature-auth ไม่สามารถนำเข้า feature-profile ได้เว้นแต่จะระบุใน dependencies
การปรับใช้ iOS สร้างขึ้นบน Swift Package Manager: แต่ละสไลด์เป็นแพ็กเกจ Swift พร้อม API สาธารณะ ในโปรเจกต์ TCA สไลด์ feature.auth ประกอบด้วย Reducer, Store, View และไคลเอนต์ API ของตัวเอง ตาม Swift Community Survey 2024, 28% ของโปรเจกต์ iOS ที่ใช้ TCA ใช้สถาปัตยกรรมสไลด์ที่ใกล้เคียงกับ FSD
ปัญหาหลักของการปรับใช้ FSD บนมือถือคือการทำซ้ำของเลเยอร์ shared ในการพัฒนามือถือ คอมโพเนนต์ UI (shared/ui) มักขึ้นอยู่กับแพลตฟอร์ม (Android Views vs Jetpack Compose vs SwiftUI) ซึ่งจำเป็นต้องมีโมดูล shared แยกต่างหากสำหรับแต่ละเทคโนโลยี ใน FSD เลเยอร์ shared มักจะเป็นอิสระจากแพลตฟอร์ม (ยูทิลิตี, การกำหนดค่า) ในขณะที่ UI-kit จะถูกย้ายไปยังโมดูลแยกต่างหากหรือไลบรารีคอมโพเนนต์
ข้อดี ของ FSD จะเห็นได้ชัดในโปรเจกต์ขนาดใหญ่ที่มีนักพัฒนา 10+ คน นักพัฒนาหรือทีมแต่ละคนทำงานบนสไลด์ของตนเองโดยไม่แตะโค้ดของผู้อื่น ความขัดแย้งใน git ลดลง 40–60% (ข้อมูลจากกรณีศึกษา feature-sliced.design) ฟีเจอร์ใหม่ถูกเพิ่มโดยไม่เสี่ยงต่อการทำให้ฟีเจอร์ที่มีอยู่เสียหาย ตราบใดที่ใช้เฉพาะ API สาธารณะของสไลด์เท่านั้น การรีแฟกเตอร์หนึ่งฟีเจอร์ไม่จำเป็นต้องเปลี่ยนแปลงฟีเจอร์อื่น — เพียงแค่เขียน ui/model/api ภายในสไลด์เดียวใหม่โดยรักษา API สาธารณะไว้
| ด้าน | FSD | Feature-based (ไม่มี FSD) | สถาปัตยกรรมแบบเลเยอร์ |
|---|---|---|---|
| การแยกฟีเจอร์ | เข้มงวด | ปานกลาง | ต่ำ |
| การพัฒนาแบบขนาน | 10+ ทีม | 3–5 ทีม | 1–2 ทีม |
| การนำกลับมาใช้ระหว่างโปรเจกต์ | ใช่ (แพ็กเกจสไลด์) | ผ่าน copy-paste เท่านั้น | ผ่านโมดูล shared |
| อุปสรรคในการเริ่มต้น | สูง | ต่ำ | ปานกลาง |
| การแยก Gradle (Android) | เนทีฟ (โมดูล) | เนทีฟ (โมดูล) | อ่อนแอ |
ข้อเสีย ของ FSD — การซ้อนมากเกินไปสำหรับโปรเจกต์ขนาดเล็ก ถ้าแอปพลิเคชันประกอบด้วย 3–5 หน้าจอ เจ็ดเลเยอร์และการแบ่งเซกเมนต์ภายในแต่ละสไลด์สร้างโค้ดสำหรับการจัดระเบียบมากกว่าตัวแอปพลิเคชันเอง อุปสรรคในการเริ่มต้นสูง: นักพัฒนาใหม่ใช้เวลา 2–4 สัปดาห์ในการเรียนรู้วิธีการ นอกจากนี้ FSD เข้ากันไม่ได้ดีกับการทำต้นแบบอย่างรวดเร็ว — การทำต้นแบบต้องการการนำเข้าข้ามเลเยอร์บ่อยครั้ง ซึ่งถูกห้ามใน FSD และทำให้รอบการทำงานช้าลง
แนะนำให้เริ่มต้นด้วยโครงสร้าง Feature-based ที่ง่ายกว่าและย้ายไปยัง FSD เมื่อจำนวนหน้าจอเกิน 20 และทีมมีนักศึกษามากกว่า 5 คน
คำถามที่พบบ่อย
สถาปัตยกรรม Feature-based จัดกลุ่มโค้ดตามฟีเจอร์โดยไม่มีกฎการนำเข้าที่เข้มงวด — feature Auth สามารถนำเข้า feature Profile อื่นโดยไม่มีข้อจำกัด FSD เพิ่มลำดับชั้นของเลเยอร์และกฎ «เลเยอร์มองลงไปด้านล่างเท่านั้น» ใน Feature-based entity และ feature สามารถอยู่ในระดับเดียวกันและนำเข้าซึ่งกันและกัน; ใน FSD entity อยู่ด้านล่าง feature และ feature นำเข้า entity ไม่ใช่กลับกัน Feature-based เหมาะสำหรับโปรเจกต์ขนาดเล็ก FSD สำหรับโปรเจกต์ขนาดใหญ่
การแยกสไลด์ช่วยให้การทดสอบหน่วยง่ายขึ้น — แต่ละสไลด์ถูกทดสอบอย่างอิสระโดยการจำลองการพึ่งพาของเลเยอร์ด้านล่าง สำหรับ feature auth การจำลอง entity user ก็เพียงพอแล้ว การทดสอบแบบบูรณาการตรวจสอบ API สาธารณะของสไลด์ ใน Android โมดูล Gradle ของฟีเจอร์มีไดเรกทอรีทดสอบของตัวเองพร้อมการทดสอบ Reducer, ไคลเอนต์ API และ UI (ผ่าน Compose Test) ใน iOS แพ็กเกจสไลด์รวมการทดสอบของทุกเซกเมนต์
ได้ FSD ทำงานได้ดีกับ Jetpack Compose โดยเฉพาะในโปรเจกต์ Android แบบหลายโมดูล แต่ละสไลด์เป็นโมดูล Gradle แยกต่างหากพร้อม API สาธารณะผ่านคำสั่ง exported เลเยอร์ features มีฟีเจอร์ Composable (LoginFeature, ProductListFeature) เลเยอร์ entities มีคลาสข้อมูลและ Repository และ shared มี UI-kit (MaterialTheme-wrapper, คอมโพเนนต์ที่กำหนดเอง) FSD แนะนำสำหรับโปรเจกต์ Compose ขนาดใหญ่ที่มีนักพัฒนา 5+ คน
เลเยอร์ที่บังคับคือ app, shared, entities และ features ส่วนที่เหลือ (processes, pages, widgets) ไม่บังคับและเพิ่มตามความจำเป็น ในการพัฒนามือถือ เลเยอร์ pages มักถูกรวมกับการกำหนดเส้นทางนำทาง และ widgets ถูกแทนที่ด้วย shared/ui-kit กระบวนการ (processes) มักไม่ใช้ในโปรเจกต์มือถือ — บทบาทของมันถูกเติมเต็มโดยเลเยอร์โดเมนหรือลอจิกทางธุรกิจใน ViewModel สิ่งสำคัญคือปฏิบัติตามกฎลำดับชั้นการนำเข้า
FSD ยืมจาก DDD แนวคิดของ Bounded Context และ Ubiquitous Language แต่ละสไลด์สอดคล้องกับ bounded context — ขอบเขตที่คำศัพท์มีความหมายที่ชัดเจน ภายในสไลด์ ใช้ภาษาที่เป็นหนึ่งเดียว (ubiquitous language) ที่ทั้งนักพัฒนาและนักวิเคราะห์ธุรกิจเข้าใจ ตัวอย่างเช่น ในสไลด์ auth คำศัพท์ «เข้าสู่ระบบ», «รหัสผ่าน», «โทเคน» มีความหมายเดียวกันสำหรับสมาชิกทุกคนในทีม ซึ่งลดความเข้าใจผิดระหว่างนักวิเคราะห์และนักพัฒนาได้ 30–50%
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม