Feature Sliced Design là một hướng tiếp cận hiện đại để tổ chức code frontend theo tính năng, nghiệp vụ và mức độ phụ thuộc. Thay vì chỉ chia mã nguồn theo loại file như components, services, hooks hay utils, phương pháp này chia ứng dụng thành các layer và slice rõ ràng. Nhờ đó, dự án dễ mở rộng hơn, dễ bảo trì hơn và phù hợp với các team frontend phát triển sản phẩm dài hạn.
Với lập trình viên frontend và kiến trúc sư phần mềm, cấu trúc thư mục ảnh hưởng trực tiếp đến tốc độ onboard, khả năng review code, mức độ tái sử dụng và rủi ro khi refactor. Bài viết này sẽ giúp bạn hiểu Feature Sliced Design, cách tổ chức thư mục, lợi ích, điểm khác biệt với kiến trúc truyền thống và ví dụ áp dụng thực tế.
Feature Sliced Design là gì trong frontend?
Feature Sliced Design là phương pháp tổ chức mã nguồn frontend dựa trên hai khái niệm chính: layer và slice. Layer là tầng trách nhiệm của ứng dụng, còn slice là một lát cắt chức năng hoặc nghiệp vụ cụ thể. Cách chia này giúp code không bị gom lẫn lộn chỉ vì cùng là component, API hay helper.
Trong cấu trúc truyền thống, nhiều dự án bắt đầu bằng các thư mục như components, pages, services, store và utils. Cách này đơn giản ở giai đoạn đầu, nhưng khi sản phẩm có nhiều màn hình và nhiều luồng nghiệp vụ, developer thường phải tìm một chức năng ở nhiều nơi khác nhau.
Với Feature Sliced Design, code được đặt gần ngữ cảnh sử dụng hơn. Ví dụ, tính năng thêm sản phẩm vào giỏ hàng có thể có UI, logic xử lý và model riêng trong cùng một slice. Khi cần sửa nghiệp vụ này, lập trình viên biết rõ nên bắt đầu từ đâu và phạm vi ảnh hưởng nằm ở đâu.
Layer và slice hoạt động như thế nào?

Layer thể hiện cấp độ trách nhiệm. Một dự án thường có các layer như app, pages, widgets, features, entities và shared. App dùng để khởi tạo ứng dụng; pages đại diện cho màn hình; widgets là khối giao diện lớn; features là hành động người dùng; entities là thực thể nghiệp vụ; shared là phần dùng chung.
Slice là phần nhỏ bên trong layer, thường gắn với miền cụ thể. Trong features có thể có login-by-email, add-to-cart hoặc update-profile. Trong entities có thể có user, product, order hoặc task. Cách đặt tên này giúp cấu trúc dự án phản ánh nghiệp vụ thay vì chỉ phản ánh kỹ thuật.
Cấu trúc thư mục phổ biến của Feature Sliced Design
Một dự án áp dụng Feature Sliced Design có thể tổ chức theo cấu trúc sau:
src/
app/
providers/
routes/
pages/
home/
product-detail/
widgets/
header/
product-list/
features/
login-by-email/
add-product-to-cart/
entities/
user/
product/
shared/
ui/
api/
lib/
Không phải dự án nào cũng cần dùng đầy đủ mọi layer. Với dự án nhỏ, bạn có thể bắt đầu bằng app, pages và shared. Khi nghiệp vụ lớn dần, hãy thêm features, entities hoặc widgets. Điều quan trọng là không tạo cấu trúc phức tạp trước khi dự án thật sự cần.
Vai trò của từng layer

Layer app chứa router, provider, global style, cấu hình state hoặc theme. Đây là nơi thiết lập nền tảng cho ứng dụng, không nên chứa logic nghiệp vụ chi tiết. Layer pages đại diện cho các route hoặc màn hình chính, tập trung vào việc lắp ráp các widget, feature và entity.
Layer widgets chứa các khối giao diện lớn như header, sidebar, product list hoặc checkout summary. Widgets giúp page gọn hơn và làm cho từng phần giao diện có trách nhiệm rõ ràng. Đây là layer hữu ích khi một màn hình có nhiều khối phức tạp.
Layer features chứa các hành động người dùng có thể thực hiện, chẳng hạn đăng nhập, lọc sản phẩm, thêm vào giỏ hàng hoặc cập nhật hồ sơ. Layer entities đại diện cho các thực thể cốt lõi như user, product, order hoặc task. Layer shared chứa Button, Input, Modal, httpClient, config và helper trung lập với nghiệp vụ.
Quy tắc phụ thuộc trong Feature Sliced Design
Nguyên tắc quan trọng của Feature Sliced Design là phụ thuộc đi từ layer cao xuống layer thấp. Pages có thể dùng widgets, features, entities và shared. Widgets có thể dùng features, entities và shared. Features có thể dùng entities và shared. Entities có thể dùng shared. Shared không nên phụ thuộc ngược lên các layer phía trên.
| Layer | Vai trò chính | Ví dụ nội dung |
|---|---|---|
| app | Khởi tạo ứng dụng | router, provider, global style |
| pages | Lắp ráp màn hình | home, profile, product detail |
| widgets | Khối giao diện lớn | header, product list |
| features | Hành động nghiệp vụ | login, add to cart |
| entities | Thực thể nghiệp vụ | user, product, order |
| shared | Thành phần dùng chung | button, modal, http client |
Quy tắc này giúp hạn chế import chéo, circular dependency và tình trạng module nhỏ phụ thuộc vào module lớn. Khi một slice được thay đổi, phạm vi ảnh hưởng cũng dễ dự đoán hơn. Đây là lý do Feature Sliced Design phù hợp với dự án có nhiều developer cùng phát triển.
Lợi ích của Feature Sliced Design trong dự án thực tế

Lợi ích đầu tiên của Feature Sliced Design là khả năng mở rộng. Khi sản phẩm có thêm tính năng mới, nhóm có thể tạo slice mới thay vì nhồi thêm file vào components hoặc services. Cấu trúc dự án vẫn rõ ràng ngay cả khi số lượng file tăng lên.
Lợi ích thứ hai là tăng khả năng bảo trì. Nếu cần sửa logic thêm vào giỏ hàng, developer có thể tìm trong add-product-to-cart. Nếu cần sửa dữ liệu sản phẩm, developer kiểm tra entity product. Cách chia này giảm thời gian tìm kiếm và giảm nguy cơ sửa nhầm vùng code không liên quan.
Ngoài ra, Feature Sliced Design hỗ trợ refactor an toàn hơn. Khi ranh giới module rõ ràng, team có thể thay đổi bên trong một slice mà ít ảnh hưởng đến nơi khác, miễn là public API của slice được giữ ổn định.
So sánh Feature Sliced Design với kiến trúc truyền thống
Kiến trúc truyền thống theo loại file có ưu điểm là dễ bắt đầu. Với dự án nhỏ, các thư mục components, hooks, services, store và utils giúp developer nhanh chóng biết file thuộc loại kỹ thuật nào. Tuy nhiên, cách chia này không cho thấy rõ file phục vụ phần nghiệp vụ nào.
Khi dự án lớn, components có thể chứa hàng trăm file không cùng ngữ cảnh. Services có thể chứa API của nhiều domain khác nhau. Developer mới muốn hiểu một chức năng phải mở nhiều thư mục, từ UI đến API, state và helper.
Feature Sliced Design ưu tiên câu hỏi “code này thuộc phần nào của sản phẩm?” thay vì “code này là loại file gì?”. Cách tiếp cận này gần hơn với tư duy domain và giúp frontend có cấu trúc bền vững hơn. Tuy nhiên, nó cũng cần quy ước rõ về đặt tên, hướng phụ thuộc và public API.
Ví dụ áp dụng Feature Sliced Design trong dự án React
Giả sử bạn xây dựng ứng dụng bán hàng có chức năng xem sản phẩm và thêm sản phẩm vào giỏ. Cấu trúc có thể như sau:
src/
pages/product-detail/
ui/ProductDetailPage.tsx
widgets/product-info/
ui/ProductInfo.tsx
features/add-product-to-cart/
ui/AddToCartButton.tsx
model/useAddToCart.ts
entities/product/
api/productApi.ts
model/types.ts
shared/ui/
Button.tsx
Trong ví dụ này, product-detail page lắp ráp màn hình. Widget product-info hiển thị thông tin sản phẩm. Feature add-product-to-cart xử lý hành động thêm sản phẩm vào giỏ. Entity product chứa dữ liệu, API và kiểu dữ liệu liên quan đến sản phẩm. Shared chứa Button dùng chung.
Khi cần đổi cách gọi API sản phẩm, bạn kiểm tra entities/product. Khi cần đổi hành vi thêm vào giỏ, bạn kiểm tra features/add-product-to-cart. Khi cần đổi giao diện nút, bạn chỉnh shared/ui/Button. Cách tách này giúp phạm vi sửa đổi rõ ràng hơn.
Cách triển khai từng bước
Nếu dự án hiện tại đang dùng cấu trúc truyền thống, bạn không cần chuyển toàn bộ sang Feature Sliced Design trong một lần. Cách an toàn là áp dụng dần cho module mới hoặc khu vực đang refactor.
Bạn có thể bắt đầu bằng các bước sau:
- Tách shared cho UI, API client, config và helper trung lập.
- Xác định các entities quan trọng như user, product, order hoặc task.
- Chuyển các hành động nghiệp vụ rõ ràng vào layer features.
- Giữ pages ở vai trò lắp ráp, hạn chế chứa logic chi tiết.
- Dùng file index để kiểm soát public API của từng slice.
- Thống nhất quy tắc import bằng code review hoặc lint rule.
Lưu ý khi áp dụng Feature Sliced Design
Lỗi phổ biến đầu tiên là chia quá nhỏ ngay từ đầu. Nếu mọi phần đều bị tách thành slice riêng, dự án có thể rườm rà và khó điều hướng. Kiến trúc tốt là kiến trúc giúp team hiểu code nhanh hơn, không phải kiến trúc tạo nhiều thư mục nhất.
Lỗi thứ hai là đặt sai trách nhiệm layer. Ví dụ, đưa logic nghiệp vụ vào shared, để page chứa toàn bộ xử lý phức tạp hoặc để feature phụ thuộc ngược vào page. Những sai lệch này làm Feature Sliced Design mất tác dụng.
Lỗi thứ ba là import sâu vào nội bộ slice. Nếu module khác truy cập trực tiếp file bên trong thay vì thông qua public API, việc refactor sẽ khó hơn. Team nên thống nhất những gì được export ra ngoài và những gì chỉ dùng nội bộ.
Câu hỏi thường gặp về Feature Sliced Design
Feature Sliced Design có chỉ dùng cho React không?
Không. Feature Sliced Design thường được nhắc nhiều trong cộng đồng React, nhưng tư duy layer và slice có thể áp dụng cho Vue, Angular hoặc framework frontend khác.
Làm sao kiểm soát import sai layer?
Team có thể dùng quy ước code review, alias import, lint rule và tài liệu kiến trúc ngắn gọn. Với dự án lớn, rule tự động sẽ giúp phát hiện sớm import sai.
Kết luận
Feature Sliced Design là kiến trúc tổ chức code frontend hiện đại, phù hợp với các dự án cần mở rộng, bảo trì lâu dài và có nhiều lập trình viên cùng phát triển. Bằng cách chia ứng dụng thành layer và slice, phương pháp này giúp code rõ trách nhiệm, giảm phụ thuộc chéo và hỗ trợ refactor an toàn hơn xem thêm tại Top 10 Nhôm Kính.
Tuy nhiên, kiến trúc chỉ hiệu quả khi được áp dụng linh hoạt. Với dự án nhỏ, hãy bắt đầu đơn giản. Với dự án lớn, hãy thống nhất quy tắc phụ thuộc, public API và cách đặt tên slice ngay từ đầu. Khi dùng đúng mức, Feature Sliced Design giúp đội ngũ xây dựng frontend bền vững, dễ hiểu và dễ phát triển trong tương lai.
