Giải thích Feature-Sliced Design là gì — một phương pháp kiến trúc mô-đun frontend dựa trên việc chia dự án theo tính năng kinh doanh thay vì lớp kỹ thuật. Khác với kiến trúc lớp cổ điển (controller, service, repository), FSD nhóm mã theo khả năng chức năng của ứng dụng: mỗi tính năng chứa logic riêng, UI và dữ liệu. Theo khảo sát State of Frontend 2024, 23% nhà phát triển React sử dụng FSD làm phương pháp kiến trúc chính, khiến nó trở thành phương pháp phổ biến thứ hai sau cấu trúc Feature-based thuần túy.
Điểm chính
Feature-Sliced Design (FSD) là một phương pháp kiến trúc ứng dụng frontend lần đầu tiên được đề xuất vào năm 2021 bởi cộng đồng feature-sliced.design. Ý tưởng cốt lõi của FSD là nhóm mã theo tính năng kinh doanh (slice), mỗi slice là một đơn vị tự cung cấp: chứa logic kinh doanh riêng, giao diện người dùng, tương tác API, mô hình dữ liệu và kiểm thử. Điều này phân biệt FSD với kiến trúc lớp cổ điển nơi mã được chia theo tiêu chí kỹ thuật (controller, service, repository).
Phương pháp này vay mượn các khái niệm từ Domain-Driven Design (DDD) và Bounded Context: mỗi tính năng của ứng dụng là một bounded context riêng biệt với ranh giới rõ ràng. Các thay đổi bên trong một tính năng không được làm hỏng các tính năng khác nếu chúng chỉ sử dụng API công khai của slice. Theo khảo sát State of Frontend 2024, FSD đứng thứ hai về mức độ phổ biến trong số các kiến trúc React (23%), chỉ sau cấu trúc Feature-based không chính thức (31%).
Trong phát triển di động, FSD thích ứng với đặc thù của mô-đun Android và framework iOS. Tại IT Sectr, chúng tôi sử dụng FSD cho các dự án có 10+ màn hình và 3+ nhóm — phương pháp này cho phép phát triển tính năng độc lập và giảm xung đột git xuống 40% so với kho lưu trữ đơn không có ranh giới slice.
FSD định nghĩa bảy lớp phân cấp, mỗi lớp chứa mã ở một mức trừu tượng nhất định. Quy tắc kiến trúc chính là các lớp chỉ có thể import mã từ các lớp bên dưới. Vi phạm quy tắc này (import lớp features vào entities) được coi là lỗi kiến trúc và bị chặn bởi linter.
| Lớp | Mục đích | Import |
|---|---|---|
| app | Khởi tạo ứng dụng, nhà cung cấp, kiểu toàn cục, định tuyến | Bất kỳ lớp nào |
| processes | Quy trình kinh doanh kết hợp nhiều tính năng (onboarding, thanh toán) | pages, features, entities, shared |
| pages | Tổ hợp tính năng trên trang, định tuyến trang | features, entities, shared |
| features | Kịch bản người dùng: biểu mẫu đăng nhập, danh sách yêu thích, bộ lọc tìm kiếm | entities, shared |
| entities | Thực thể kinh doanh: User, Product, Order, Cart | shared |
| widgets | Thành phần UI tổng hợp: Header, Sidebar, ArticleCard | shared, entities |
| shared | Tiện ích, UI-kit, client API, cấu hình — độc lập với logic kinh doanh | Chỉ thư viện bên ngoài |
Ví dụ cấu trúc thư mục của dự án FSD:
src/
├── app/ // Lớp ứng dụng
│ ├── providers/
│ ├── router/
│ └── styles/
├── pages/ // Trang — tổ hợp tính năng
│ └── main/
├── features/ // Tính năng — kịch bản người dùng
│ ├── auth/ // Slice «Xác thực»
│ │ ├── ui/
│ │ ├── model/
│ │ └── api/
│ └── productList/ // Slice «Danh sách sản phẩm»
│ ├── ui/
│ └── model/
├── entities/ // Thực thể kinh doanh
│ ├── user/
│ └── product/
├── widgets/ // Thành phần tổng hợp
│ └── header/
└── shared/ // Tiện ích dùng chung và UI-kit
└── ui/Quy tắc «các lớp chỉ nhìn xuống dưới» là nền tảng của FSD. Nếu feature auth import entity user — điều đó đúng. Nếu entity user bắt đầu import feature auth — đó là phụ thuộc vòng tròn và vi phạm cô lập. Để đảm bảo quy tắc này, các plugin ESLint (eslint-plugin-fsd) hoặc linter tùy chỉnh của API công khai slice được sử dụng.
Slice — đơn vị nhóm chính trong FSD, tương ứng với một tính năng hoặc thực thể kinh doanh. Mỗi slice nằm trong một trong bảy lớp (features, entities, widgets, pages) và chứa bộ mã hoàn chỉnh để triển khai chức năng cụ thể: thành phần UI, mô hình dữ liệu, client API, hằng số và kiểm thử.
Ranh giới slice được xác định bởi miền kinh doanh: feature auth bao gồm mọi thứ liên quan đến xác thực (biểu mẫu đăng nhập, biểu mẫu đăng ký, đặt lại mật khẩu); entity user bao gồm mô hình User, UserRepository và tuần tự hóa. Ranh giới không được chồng chéo: nếu feature auth cần dữ liệu người dùng — nó import entity user thay vì sao chép logic. Trong phát triển di động, slice FSD thường tương ứng với mô-đun Gradle trong Android hoặc gói Swift trong iOS.
Các slice bị cô lập nghiêm ngặt: cấu trúc bên trong của một slice không thể nhìn thấy được đối với các slice khác. Để tương tác giữa các slice, API công khai được sử dụng — tệp index.ts/index.js chỉ xuất những gì được phép sử dụng bên ngoài. Mọi thứ khác là mô-đun riêng tư. Cách tiếp cận này ngăn chặn các phụ thuộc ngẫu nhiên và đơn giản hóa việc tái cấu trúc: thay đổi triển khai riêng tư của một slice không ảnh hưởng đến các slice khác.
Bên trong mỗi slice FSD, mã được tổ chức thêm theo phân đoạn — các danh mục kỹ thuật lặp lại trong tất cả các slice. Bộ phân đoạn tiêu chuẩn bao gồm ui (thành phần giao diện), model (logic kinh doanh, Store, Actions, Reducer), api (yêu cầu máy chủ, biến đổi), lib (tiện ích và trình trợ giúp) và config (cấu hình tính năng).
| Phân đoạn | Nội dung | Ví dụ |
|---|---|---|
| ui/ | Thành phần React/Vue/SwiftUI, kiểu, Storybook | LoginForm.tsx, login.module.css |
| model/ | Store, Reducer, Actions, kiểu, hợp đồng | LoginStore.ts, authReducer.ts |
| api/ | Client HTTP, biến đổi, gọi RPC | authApi.ts, loginMutation.ts |
| lib/ | Hàm trợ giúp, trình xác thực | validateEmail.ts, formatPhone.ts |
| config/ | Hằng số, cấu hình tính năng | authConfig.ts, endpoints.ts |
Phân đoạn là khuyến nghị, không phải quy tắc nghiêm ngặt. Nếu slice nhỏ, các phân đoạn có thể được hợp nhất. Đối với slice lớn (tính năng có 10+ tệp), phân đoạn là bắt buộc — nếu không có nó, cấu trúc bên trong nhanh chóng biến thành «rổ» gồm 50 tệp, nơi việc tìm thành phần cần thiết mất nhiều phút. Trong phát triển di động, các phân đoạn thường được thay thế bằng cấu trúc tệp theo loại: mỗi tính năng là một tệp Swift riêng biệt hoặc lớp Kotlin với các kiểu bên trong.
Trong phát triển di động, FSD thích ứng với các đặc điểm nền tảng — cấu trúc mô-đun của Android (mô-đun Gradle) và Swift Package Manager. Thích ứng Android giả định rằng mỗi slice là một mô-đun Gradle riêng biệt với build.gradle riêng. Các mô-đun feature-auth, feature-profile, entity-user, shared-ui được cô lập với nhau ở cấp độ xây dựng: feature-auth không thể import feature-profile trừ khi được chỉ định trong dependencies.
Thích ứng iOS được xây dựng trên Swift Package Manager: mỗi slice là một gói Swift với API công khai. Trong các dự án TCA, slice feature.auth chứa Reducer, Store, View và client API riêng. Theo Swift Community Survey 2024, 28% dự án iOS với TCA sử dụng kiến trúc slice gần với FSD.
Vấn đề chính của việc thích ứng FSD trên di động là sự trùng lặp của lớp shared. Trong phát triển di động, các thành phần UI (shared/ui) thường phụ thuộc vào nền tảng (Android Views vs Jetpack Compose vs SwiftUI), đòi hỏi các mô-đun shared riêng biệt cho mỗi công nghệ. Trong FSD, lớp shared thường độc lập với nền tảng (tiện ích, cấu hình), trong khi UI-kit được chuyển sang mô-đun riêng biệt hoặc thư viện thành phần.
Ưu điểm của FSD trở nên rõ rệt trong các dự án lớn với 10+ nhà phát triển. Mỗi nhà phát triển hoặc nhóm làm việc trên slice riêng của mình mà không chạm vào mã của người khác. Xung đột git giảm 40–60% (dữ liệu từ nghiên cứu điển hình của feature-sliced.design). Các tính năng mới được thêm vào mà không có rủi ro làm hỏng tính năng hiện có, miễn là chúng chỉ sử dụng API công khai của slice. Tái cấu trúc một tính năng không yêu cầu thay đổi ở các tính năng khác — chỉ cần viết lại ui/model/api bên trong một slice trong khi giữ nguyên API công khai.
| Khía cạnh | FSD | Feature-based (không FSD) | Kiến trúc lớp |
|---|---|---|---|
| Cô lập tính năng | Nghiêm ngặt | Trung bình | Thấp |
| Phát triển song song | 10+ nhóm | 3–5 nhóm | 1–2 nhóm |
| Tái sử dụng giữa dự án | Có (gói slice) | Chỉ qua copy-paste | Qua mô-đun shared |
| Rào cản gia nhập | Cao | Thấp | Trung bình |
| Cô lập Gradle (Android) | Gốc (mô-đun) | Gốc (mô-đun) | Yếu |
Nhược điểm của FSD — lồng ghép quá mức cho dự án nhỏ. Nếu ứng dụng gồm 3–5 màn hình, bảy lớp và phân đoạn bên trong mỗi slice tạo ra nhiều mã tổ chức hơn chính ứng dụng. Rào cản gia nhập cao: nhà phát triển mới mất 2–4 tuần để học phương pháp. Ngoài ra, FSD tương thích kém với tạo mẫu nhanh — tạo mẫu yêu cầu import xuyên lớp thường xuyên, bị cấm trong FSD và làm chậm các vòng lặp.
Khuyến nghị bắt đầu với cấu trúc Feature-based đơn giản hơn và chuyển sang FSD khi số lượng màn hình vượt quá 20 và nhóm vượt quá 5 nhà phát triển.
Câu hỏi thường gặp
Kiến trúc Feature-based nhóm mã theo tính năng mà không có quy tắc import nghiêm ngặt — feature Auth có thể import feature Profile khác mà không bị hạn chế. FSD thêm phân cấp lớp và quy tắc «các lớp chỉ nhìn xuống dưới». Trong Feature-based, entity và feature có thể ở cùng cấp và import lẫn nhau; trong FSD, entity nằm dưới feature, và feature import entity, không phải ngược lại. Feature-based phù hợp cho dự án nhỏ, FSD cho dự án lớn.
Cô lập slice đơn giản hóa kiểm thử đơn vị — mỗi slice được kiểm thử độc lập bằng cách mô phỏng các phụ thuộc của lớp bên dưới. Đối với feature auth, chỉ cần mô phỏng entity user. Kiểm thử tích hợp xác minh API công khai của slice. Trong Android, mô-đun Gradle của một tính năng chứa thư mục kiểm thử riêng với kiểm thử Reducer, client API và UI (qua Compose Test). Trong iOS, gói slice bao gồm kiểm thử của tất cả các phân đoạn.
Có, FSD kết hợp tốt với Jetpack Compose, đặc biệt trong các dự án Android đa mô-đun. Mỗi slice là một mô-đun Gradle riêng biệt với API công khai qua chỉ thị exported. Lớp features chứa các tính năng Composable (LoginFeature, ProductListFeature), lớp entities chứa các lớp dữ liệu và Repository, shared chứa UI-kit (MaterialTheme-wrapper, thành phần tùy chỉnh). FSD được khuyến nghị cho các dự án Compose lớn với 5+ nhà phát triển.
Các lớp bắt buộc là app, shared, entities và features. Các lớp còn lại (processes, pages, widgets) là tùy chọn và được thêm khi cần. Trong phát triển di động, lớp pages thường được hợp nhất với định tuyến điều hướng, và widgets được thay thế bằng shared/ui-kit. Các quy trình (processes) thường không được sử dụng trong các dự án di động — vai trò của chúng được đảm nhận bởi lớp miền hoặc logic kinh doanh trong ViewModel. Điều chính là tuân thủ quy tắc phân cấp import.
FSD vay mượn từ DDD các khái niệm Bounded Context và Ubiquitous Language. Mỗi slice tương ứng với một bounded context — ranh giới trong đó các thuật ngữ có ý nghĩa rõ ràng. Bên trong slice, một ngôn ngữ thống nhất (ubiquitous language) được sử dụng, dễ hiểu cho cả nhà phát triển và nhà phân tích kinh doanh. Ví dụ, trong slice auth, các thuật ngữ «đăng nhập», «mật khẩu», «token» có cùng ý nghĩa cho tất cả thành viên nhóm, giảm hiểu lầm giữa nhà phân tích và nhà phát triển từ 30–50%.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm