我们来解释什么是Feature-Sliced Design——一种前端模块化架构方法论,基于按业务功能而非技术层划分项目。与经典分层架构(控制器、服务、仓储)不同,FSD按应用的功能能力分组代码:每个功能包含自己的逻辑、UI和数据。根据State of Frontend 2024调查数据,23%的React开发者将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定义了七个层次,每层包含特定抽象级别的代码。主要架构规则——层只能从下层导入代码。违反此规则(在entities中导入features层)被视为架构错误,会被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的基石。如果auth功能导入user实体——这是正确的。如果user实体开始导入auth功能——这是循环依赖和违反隔离。为确保规则的遵守,使用ESLint插件(eslint-plugin-fsd)或切片公共API的自定义linter。
切片(slice)——FSD中的基本分组单元,对应一个业务功能或实体。每个切片位于七层之一(features、entities、widgets、pages)中,包含实现特定功能的完整代码集:UI组件、数据模型、API客户端、常量和测试。
切片的边界由业务领域确定:auth功能包括与身份验证相关的一切(登录表单、注册表单、密码重置);user实体包括User模型、UserRepository和序列化。边界不应重叠:如果auth功能需要用户数据——它导入user实体,而不是复制逻辑。在移动开发中,FSD切片通常对应Android中的Gradle模块或iOS中的Swift包。
切片严格隔离:一个切片的内部结构对其他切片不可见。切片之间的交互使用公共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适配假设每个切片是一个带有自己build.gradle的独立Gradle模块。feature-auth、feature-profile、entity-user、shared-ui模块在编译级别相互隔离:除非在dependencies中指定,否则feature-auth不能导入feature-profile。
iOS适配基于Swift Package Manager:每个切片是带有公共API的Swift包。在TCA项目中,feature.auth切片包含自己的Reducer、Store、View和API客户端。根据Swift Community Survey 2024的数据,28%使用TCA的iOS项目采用接近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团队 |
| 项目间重用 | 是(切片包) | 仅通过复制粘贴 | 通过shared模块 |
| 入门门槛 | 高 | 低 | 中等 |
| Gradle隔离(Android) | 原生(模块) | 原生(模块) | 弱 |
缺点 FSD——对小项目过度嵌套。如果应用由3-5个屏幕组成,七层和每个切片内的分段产生的组织代码比应用本身还多。入门门槛高:新开发者花费2-4周学习该方法论。FSD也与快速原型制作兼容性差——原型需要频繁的跨层导入,这在FSD中被禁止并减慢迭代。
建议从更简单的Feature-based结构开始,当屏幕数量超过20且团队超过5名开发者时迁移到FSD。
常见问题
Feature-based架构按功能分组代码,没有严格的导入规则——Auth功能可以无限制地导入另一个Profile功能。FSD增加了层的层次结构和层只向下看规则。在Feature-based中,实体和功能可以在同一层并相互导入;在FSD中,实体位于功能之下,功能导入实体,反之不行。Feature-based适用于小项目,FSD适用于大项目。
切片的隔离简化了模块化测试——每个切片通过替换下层依赖独立测试。对于auth功能,只需mock user实体。集成测试检查切片的公共API。在Android Gradle模块中,功能包含自己的测试目录,包含Reducer、API客户端和UI的测试(通过Compose Test)。在iOS中,切片包包含所有段的测试。
可以,FSD与Jetpack Compose配合良好,特别是在多模块Android项目中。每个切片是通过exported指令带有公共API的独立Gradle模块。features层包含Composable功能(LoginFeature、ProductListFeature),entities层包含数据类和Repository,shared包含UI-kit(MaterialTheme-wrapper、自定义组件)。FSD推荐用于5+开发者的大型Compose项目。
必需的层是app、shared、entities和features。其余的(processes、pages、widgets)是可选的,根据需要添加。在移动开发中,pages层通常与导航路由合并,widgets被shared/ui-kit替代。流程(processes)通常不在移动项目中使用——它们的角色由domain层或ViewModel中的业务逻辑承担。最重要的是遵守导入层次规则。
FSD从DDD借用了Bounded Context和Ubiquitous Language的概念。每个切片对应一个bounded context——一个内部术语具有明确含义的边界。切片内部使用统一的语言(ubiquitous language),开发人员和业务分析师都能理解。例如,在auth切片中,登录、密码、令牌等术语对团队所有成员具有相同含义,这使分析师和开发人员之间的误解减少30-50%。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。