PawHaven 是一个基于 React + Node.js 的开源全栈项目。通过它,你可以学习如何从 0 开始构建一个企业级应用:从前端到后端,从代码到 CI/CD,再到云部署,涵盖完整的最佳实践。
本文就是基于 PawHaven 的架构与实践经验整理而成,如果你想查看更多可运行的代码示例,可前往 GitHub仓库 深入了解,并且给一个 star ⭐️。
1. 项目背景与设计初衷
PawHaven 在设计之初,我就明确了几个关键目标:
- 高扩展性:未来可能增加新的功能模块,如捐赠系统、志愿者管理、移动端应用等。
- 高可维护性:支持长期运营和多人协作,降低后期维护成本。
- 全球贡献者友好:项目开源后可能会有来自不同地区的贡献者,代码结构必须清晰、标准化,便于快速上手。
这些目标直接决定了 PawHaven 从一开始就不能沿用临时、零散的项目结构,而必须采用企业级的架构设计。
2. 常见项目结构及其局限性
2.1 传统"按功能类型拆分"结构
许多项目教程推荐如下结构:
js
src
├─ components
├─ assets
├─ pages
├─ utils
├─ apis
├─ store
这种结构的优点是简单直观,适合小型项目或快速原型开发。但对于复杂大型应用,存在以下
局限性:
- 功能逻辑分散:一个业务模块的代码可能散落在
pages
、store
、components
、services
中。 - 修改成本高:每次修改或调试功能,需要在多个目录搜索相关文件。
- 业务边界模糊:容易导致模块耦合,团队协作效率低,难以分工。
- 难以扩展:新增功能时,可能需要在多个目录创建文件,增加复杂度。
- 维护困难:对于大型历史项目,这种结构会显著增加维护成本和团队沟通难度。
2.2 DDD( Domain-Driven Design )模式
企业级复杂项目通常采用 业务驱动设计(DDD):
- 一级目录是业务模块(feature)
- 每个 feature 自包含所有相关逻辑:组件、页面、状态、服务、类型
- shared 模块仅存放跨业务、可复用的通用能力
优点:
- 模块自包含:修改 feature 时无需跨目录查找
- 业务边界清晰:团队成员可以独立开发或测试模块
- 高扩展性:新业务模块可以平滑加入
- 易于维护:降低了代码耦合度,提升了可读性
2.3 PawHaven 的 DDD 实践
在 PawHaven 中,前端采用 feature-first 的目录设计:
js
user
├─ src
│ ├─ features
│ │ ├─ RescueDetail
│ │ │ ├─ index.tsx
│ │ │ ├─ types.ts
│ │ │ ├─ rescueDetailSlice.ts
│ │ │ ├─ components
│ │ │ │ ├─ RescueTimeline.tsx
│ │ │ │ ├─ AnimalBasicInfo.tsx
│ │ │ │ └─ RescueInteraction.tsx
│ │ │ └─ apis
│ │ │ ├─ queries.ts
│ │ │ └─ request.ts
│ │ ├─ ReportStray
│ │ │ ├─ index.tsx
│ │ │ ├─ types.ts
│ │ │ ├─ constants.ts
│ │ │ ├─ reportStraySlice.ts
│ │ │ ├─ components
│ │ │ │ └─ ReportForm.tsx
│ │ │ └─ apis
│ │ │ ├─ requests.ts
│ │ │ └─ queries.ts
feature目录下,每个业务模块(如 RescueDetail
、ReportStray
)都包含:
- 组件(components)
- 页面(index.tsx)
- 状态管理(rescueDetailSlice.ts)
- API 请求(apis)
- 类型定义(types.ts)
- 常量(constants.ts)
这种设计使得每个业务模块高度自包含,便于团队成员独立开发和维护。
3.组件化分层策略
在企业级项目中,随着功能越来越复杂,光有 DDD 的业务划分还不够,还需要进一步的 分层策略。在 PawHaven 中,我把「分层」不仅仅理解为 UI 组件的层次,而是所有代码单元(组件、hooks、服务、工具函数等)的合理归类和边界控制。
具体来说,PawHaven 的分层策略包括:
3.1 Feature 层(feature 内部)
- 紧密绑定业务逻辑的内容。
- 每个 feature 内部自包含页面、业务组件、hooks、服务、types 等。
- 示例:report-animal/ReportForm.tsx、report-animal/hooks/useReportAnimal.ts。
3.2 跨 Feature 层(应用级)
- 在不同业务模块之间共享,但仍然带有一定业务语境的内容。
- 例如用户信息卡片、导航菜单、布局组件,或与多个业务相关的 hooks。
- 通常放在应用层级目录下,而不是某个 feature 内部。
3.3 Shared 层(与业务无关的通用层)
- 完全与业务逻辑无关的内容,包括 UI Dumb 组件(Button、Modal、Input)、工具函数、通用 hooks、常量、主题配置、国际化文案等。
- 这些内容可以在整个 monorepo 内复用,未来也能很容易抽离为独立的库。
通过这种 组件化分层策略,PawHaven 的代码结构既保证了业务边界清晰,又确保了通用能力的复用性,从而达到了高扩展性与高可维护性的目标。
4. 单体结构 vs Monorepo
在企业级项目中,如何管理多个应用和模块是一项关键决策。常见的有两种方式:单体结构(Multi-Repo) 和 Monorepo。
4.1 单体结构(Multi-Repo)
单体结构是最常见的方式:每个服务或者业务一个仓库,工具库单独一个仓库。
优点:
- 仓库独立,权限控制清晰
- 各团队可独立开发与发布
- 初期上手成本低
缺点:
- 跨项目共享代码困难,经常出现重复造轮子
- 依赖管理分散,容易出现版本冲突
- 跨仓库重构复杂,维护成本高
实际案例: 像 Netflix、Spotify 的微服务体系,出于服务解耦的考虑,一直使用多仓库来管理各个服务。 我自己曾经深受单体结构的困扰,在多个项目间共享代码时,常常需要手动同步版本,导致依赖混乱和调试困难,同时本地开发期间,往往需要频繁切换不同仓库,极大降低了开发效率。 同时,开发人员经常由于麻烦而不愿意去更新共享库,导致各个项目间的代码质量和规范参差不齐,增加了维护难度。
4.2 Monorepo
Monorepo 是将多个应用、服务、库放在同一个代码仓库里统一管理。例如 Google 的 Monorepo 就包含了数十万开发者使用的代码。
优点:
- 代码共享方便:多个服务都能直接依赖 shared 模块
- 统一依赖和规范:ESLint、Prettier、CI/CD 流程可以一次配置全局生效
- 跨项目重构简单:改动一个 shared 模块,所有依赖应用立即受益
- 开发体验好:开发者在一个仓库内即可完成全部工作
缺点:
- 对工具链要求更高,需要额外的构建与工作区管理能力
- 随着代码库增大,构建和测试需要优化策略
实际案例:
- Google:全球最大的 Monorepo,支撑了 Gmail、Search、Ads 等所有核心产品
- Meta(Facebook):前端、移动端、后端共享同一 Monorepo
- Uber、Airbnb:逐步迁移到 Monorepo,以提升团队协作和重构能力
我当前公司所在项目经历了由单体结构到 Monorepo的整改,开发人员普遍感受到开发效率的提升以及可复用代码的增加
4.3 PawHaven 为什么选择 Monorepo
考虑到 PawHaven 的发展方向:
- 会同时有 前端用户端(pawHaven)、前端管理端(admin)、鉴权应用(auth) 等多个应用
- 同时还会有 多个后端微服务服务(NestJS) 和 shared 包
- 将来可能会有全球贡献者参与,需要快速理解代码结构并统一规范
因此,Monorepo 是最合适的选择。
在 PawHaven 中,目录结构如下:
js
PawHaven-frontend/
├── apps/ # 具体应用
│ ├── user/ # 用户端
│ ├── admin/ # 后台管理端
├── packages/ # 通用模块
│ ├── components/
│ ├── hooks/
│ ├── utils/
│ ├── constants/
│ ├── theme/
│ ├── i18n/
│ └── api-client/
├── package.json
└── pnpm-workspace.yaml
4.4. 为什么选择 pnpm 实现 Monorepo
市面上有多种 Monorepo 工具(如 Lerna、Nx、Turborepo),我最终选择了 pnpm workspace,原因是:
- 高效安装:基于硬链接和内容寻址,节省磁盘空间和安装时间,相比 npm/yarn 本地开发体验更好
- 严格依赖隔离:避免"幽灵依赖"问题,保证依赖声明清晰
- 原生支持 workspace:轻量、配置简单,直接支持 Monorepo 管理
- 与企业实践契合:pnpm 已在很多大厂和社区项目中被广泛使用
pawHaven配置:
yaml
// pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'
apps 目录下存放各个前端应用,packages 目录下存放共享模块。通过 workspace,可以轻松实现跨应用依赖和版本管理。
这样多个前端或者后端服务可以在同一仓库内协作开发,同时保持各自的独立构建与部署能力。
5. 总结
通过 PawHaven 的实践,我们可以看到,企业级项目的架构设计不仅仅是代码组织问题,更关乎可扩展性、可维护性和团队协作效率。
PawHaven 从一开始就采用 DDD / Feature-first 的模式,将每个业务模块自包含,保证业务边界清晰;通过 组件化分层策略,合理划分 Feature 层、跨 Feature 层和 Shared 层,提升模块复用性和可维护性;而 Monorepo + pnpm workspace 的方案,使前后端及共享模块统一管理,同时保持职责分离,让开发者可以快速上手并高效协作。
这样的架构设计不仅为当前开发提供便利,也为未来功能扩展、团队协作和全球贡献者的加入打下坚实基础。