1. 项目组织架构设计原则
🎯 核心原则
- 关注点分离(Separation of Concerns)
- 高内聚低耦合
- 约定优于配置
- 可发现性与一致性
- 可扩展性与可维护性
📦 模块化设计
- 按业务领域划分模块
- 清晰的依赖关系
- 独立的版本管理
- 可独立构建部署
- 接口契约明确
🔄 开发体验
- 快速上手(<30 分钟)
- 本地开发便捷
- 热重载支持
- 调试友好
- 自动化程度高
🚀 CI/CD 友好
- 增量构建支持
- 并行测试执行
- 环境隔离清晰
- 部署流程标准化
- 回滚机制完善
1.1 目录命名规范
| 类型 | 命名风格 | 示例 | 说明 |
|---|---|---|---|
| 根目录 | kebab-case | user-service, api-gateway | 小写字母,连字符分隔 |
| 源代码目录 | 固定名称 | src, app, lib | 统一使用 src |
| 测试目录 | 固定名称 | test, tests, __tests__ | 统一使用 tests |
| 配置文件 | kebab-case | docker-compose.yml, jest.config.js | 描述性命名 |
| 脚本文件 | kebab-case | build.sh, deploy-prod.sh | 动词 + 名词结构 |
2. Monorepo vs Multirepo 选择
2.1 对比分析
| 维度 | Monorepo(单仓库) | Multirepo(多仓库) |
|---|---|---|
| 代码共享 | ✅ 容易,直接引用 | ❌ 困难,需要发布包 |
| 依赖管理 | ✅ 统一版本,自动同步 | ⚠️ 分散管理,易冲突 |
| 重构便利性 | ✅ 跨项目重构简单 | ❌ 需要协调多个仓库 |
| CI/CD 效率 | ⚠️ 需要智能构建 | ✅ 独立构建,互不影响 |
| 权限控制 | ❌ 粗粒度,全有或全无 | ✅ 细粒度,按仓库控制 |
| 仓库大小 | ⚠️ 随时间增长快 | ✅ 保持较小 |
| 适用场景 | 紧密协作的团队/产品 | 独立产品/团队 |
💡 推荐方案:混合模式
- 核心业务系统:采用 Monorepo(便于共享和协作)
- 独立产品线:采用 Multirepo(独立发布和部署)
- 公共库/组件:单独仓库,发布为包(npm/Maven)
- 本指南以 Monorepo 为主:展示完整的项目组织结构
2.2 Monorepo 工具选型
📦 Nx (推荐)
- 智能构建缓存
- 受影响项目检测
- 分布式任务执行
- 支持多种技术栈
- 强大的 IDE 集成
🔧 Turborepo
- 极速构建性能
- 简单的配置文件
- 管道式任务执行
- Vercel 生态支持
- 轻量级解决方案
📚 Lerna
- 老牌 Monorepo 工具
- 版本管理成熟
- 包发布流程完善
- 社区生态丰富
- 学习资源丰富
🏗️ Bazel
- Google 内部工具开源
- 极强的可扩展性
- 多语言支持完善
- 构建结果可重现
- 学习曲线陡峭
3. 完整项目目录结构示例
3.1 企业级 Monorepo 根目录结构
enterprise-monorepo/
├── 📄 README.md # 项目总览与快速开始指南
├── 📄 LICENSE # 开源许可证
├── 📄 CHANGELOG.md # 变更日志
├── 📄 CONTRIBUTING.md # 贡献指南
├── 📄 CODE_OF_CONDUCT.md # 行为准则
├── 📄 SECURITY.md # 安全政策
│
├── 🔧 配置文件
│ ├── package.json # 根 package.json(workspaces 配置)
│ ├── pnpm-workspace.yaml # pnpm workspace 配置
│ ├── tsconfig.base.json # TypeScript 基础配置
│ ├── .eslintrc.js # ESLint 配置
│ ├── .prettierrc # Prettier 配置
│ ├── .editorconfig # 编辑器配置
│ ├── .gitignore # Git 忽略规则
│ ├── .gitattributes # Git 属性配置
│ └── .env.example # 环境变量模板
│
├── 📦 包/库(packages)
│ ├── packages/
│ │ ├── ui-components/ # 共享 UI 组件库
│ │ │ ├── src/
│ │ │ ├── tests/
│ │ │ ├── package.json
│ │ │ └── tsconfig.json
│ │ ├── utils/ # 共享工具函数库
│ │ │ ├── src/
│ │ │ ├── tests/
│ │ │ └── package.json
│ │ ├── types/ # 共享 TypeScript 类型定义
│ │ │ ├── src/
│ │ │ └── package.json
│ │ └── api-client/ # API 客户端 SDK
│ │ ├── src/
│ │ ├── tests/
│ │ └── package.json
│
├── 🖥️ 前端应用(apps/frontend)
│ ├── apps/
│ │ ├── web-admin/ # 后台管理系统
│ │ │ ├── src/
│ │ │ ├── public/
│ │ │ ├── tests/
│ │ │ ├── package.json
│ │ │ └── vite.config.ts
│ │ ├── web-mobile/ # 移动端 H5 应用
│ │ │ ├── src/
│ │ │ ├── public/
│ │ │ └── package.json
│ │ └── electron-desktop/ # 桌面客户端
│ │ ├── src/
│ │ ├── package.json
│ │ └── electron-builder.json
│
├── ⚙️ 后端服务(services/backend)
│ ├── services/
│ │ ├── api-gateway/ # API 网关服务
│ │ │ ├── src/
│ │ │ ├── tests/
│ │ │ ├── Dockerfile
│ │ │ └── package.json
│ │ ├── user-service/ # 用户服务
│ │ │ ├── src/main/java/...
│ │ │ ├── src/test/java/...
│ │ │ ├── pom.xml
│ │ │ └── Dockerfile
│ │ ├── order-service/ # 订单服务
│ │ │ ├── src/
│ │ │ ├── pom.xml
│ │ │ └── Dockerfile
│ │ ├── payment-service/ # 支付服务
│ │ │ ├── src/
│ │ │ ├── pom.xml
│ │ │ └── Dockerfile
│ │ └── notification-service/ # 通知服务
│ │ ├── src/
│ │ ├── pom.xml
│ │ └── Dockerfile
│
├── 🧪 测试(tests)
│ ├── tests/
│ │ ├── e2e/ # 端到端测试
│ │ │ ├── specs/
│ │ │ ├── fixtures/
│ │ │ └── playwright.config.ts
│ │ ├── integration/ # 集成测试
│ │ │ ├── api/
│ │ │ └── database/
│ │ ├── performance/ # 性能测试
│ │ │ ├── jmeter/
│ │ │ └── k6/
│ │ └── shared/ # 共享测试工具
│ │ ├── mocks/
│ │ ├── factories/
│ │ └── helpers/
│
├── 🚀 部署与运维(deploy)
│ ├── deploy/
│ │ ├── docker/ # Docker 配置
│ │ │ ├── Dockerfile.base
│ │ │ ├── docker-compose.dev.yml
│ │ │ ├── docker-compose.test.yml
│ │ │ └── docker-compose.prod.yml
│ │ ├── kubernetes/ # Kubernetes 配置
│ │ │ ├── base/ # 基础配置(Kustomize)
│ │ │ ├── overlays/
│ │ │ │ ├── dev/
│ │ │ │ ├── staging/
│ │ │ │ └── prod/
│ │ │ ├── charts/ # Helm Charts
│ │ │ │ ├── api-gateway/
│ │ │ │ ├── user-service/
│ │ │ │ └── ...
│ │ │ └── manifests/ # 原始 YAML
│ │ ├── scripts/ # 部署脚本
│ │ │ ├── build.sh
│ │ │ ├── deploy-dev.sh
│ │ │ ├── deploy-staging.sh
│ │ │ ├── deploy-prod.sh
│ │ │ ├── rollback.sh
│ │ │ └── cleanup.sh
│ │ └── terraform/ # 基础设施即代码
│ │ ├── modules/
│ │ ├── environments/
│ │ │ ├── dev/
│ │ │ ├── staging/
│ │ │ └── prod/
│ │ └── backend.tf
│
├── 🛠️ 工具与脚本(scripts)
│ ├── scripts/
│ │ ├── setup/ # 环境初始化脚本
│ │ │ ├── init-dev-env.sh
│ │ │ ├── install-deps.sh
│ │ │ └── setup-hooks.sh
│ │ ├── ci/ # CI 相关脚本
│ │ │ ├── run-tests.sh
│ │ │ ├── build-all.sh
│ │ │ └── publish.sh
│ │ ├── codegen/ # 代码生成脚本
│ │ │ ├── generate-api-client.sh
│ │ │ └── generate-types.sh
│ │ ├── database/ # 数据库脚本
│ │ │ ├── migrate.sh
│ │ │ ├── seed.sh
│ │ │ └── backup.sh
│ │ └── utils/ # 工具脚本
│ │ ├── clean.sh
│ │ ├── lint-all.sh
│ │ └── format-all.sh
│
├── 📚 文档(docs)
│ ├── docs/
│ │ ├── architecture/ # 架构文档
│ │ │ ├── system-design.md
│ │ │ ├── database-schema.md
│ │ │ └── api-design.md
│ │ ├── api/ # API 文档
│ │ │ ├── openapi.yaml
│ │ │ └── graphql-schema.graphql
│ │ ├── guides/ # 开发指南
│ │ │ ├── getting-started.md
│ │ │ ├── development-workflow.md
│ │ │ └── deployment-guide.md
│ │ ├── adr/ # 架构决策记录
│ │ │ ├── 001-use-monorepo.md
│ │ │ ├── 002-choose-postgresql.md
│ │ │ └── ...
│ │ └── runbook/ # 运维手册
│ │ ├── oncall-guide.md
│ │ ├── incident-response.md
│ │ └── troubleshooting.md
│
├── 🔐 安全与合规(security)
│ ├── security/
│ │ ├── policies/ # 安全策略
│ │ ├── audits/ # 安全审计报告
│ │ └── secrets/ # 密钥管理(.gitignore)
│ │ └── .env.vault
│
├── 📊 监控与日志(observability)
│ ├── observability/
│ │ ├── dashboards/ # Grafana 仪表板
│ │ ├── alerts/ # 告警规则
│ │ └── logging/ # 日志配置
│ │ └── logback-spring.xml
│
└── 🎨 资源文件(assets)
├── assets/
│ ├── images/ # 图片资源
│ ├── fonts/ # 字体文件
│ ├── icons/ # 图标资源
│ └── branding/ # 品牌素材
│ ├── logo.svg
│ └── style-guide.pdf
4. 后端服务目录规划
4.1 Java Spring Boot 项目结构
user-service/ # 用户微服务
├── 📄 README.md
├── 📄 pom.xml # Maven 配置
├── 📄 Dockerfile
├── 📄 docker-compose.yml
│
├── 📂 src/
│ ├── main/
│ │ ├── java/com/company/user/
│ │ │ ├── UserApplication.java # 启动类
│ │ │ │
│ │ │ ├── controller/ # 控制器层
│ │ │ │ ├── UserController.java
│ │ │ │ ├── AuthController.java
│ │ │ │ └── AdminUserController.java
│ │ │ │
│ │ │ ├── service/ # 服务层
│ │ │ │ ├── UserService.java
│ │ │ │ ├── UserServiceImpl.java
│ │ │ │ ├── AuthService.java
│ │ │ │ └── dto/
│ │ │ │ ├── UserDTO.java
│ │ │ │ ├── LoginRequest.java
│ │ │ │ └── RegisterRequest.java
│ │ │ │
│ │ │ ├── repository/ # 数据访问层
│ │ │ │ ├── UserRepository.java
│ │ │ │ ├── RoleRepository.java
│ │ │ │ └── entity/
│ │ │ │ ├── User.java
│ │ │ │ ├── Role.java
│ │ │ │ └── UserRole.java
│ │ │ │
│ │ │ ├── config/ # 配置类
│ │ │ │ ├── SecurityConfig.java
│ │ │ │ ├── SwaggerConfig.java
│ │ │ │ ├── RedisConfig.java
│ │ │ │ └── AsyncConfig.java
│ │ │ │
│ │ │ ├── aspect/ # AOP 切面
│ │ │ │ ├── LoggingAspect.java
│ │ │ │ └── PerformanceAspect.java
│ │ │ │
│ │ │ ├── exception/ # 异常处理
│ │ │ │ ├── GlobalExceptionHandler.java
│ │ │ │ ├── BusinessException.java
│ │ │ │ └── ErrorResponse.java
│ │ │ │
│ │ │ ├── util/ # 工具类
│ │ │ │ ├── JwtUtil.java
│ │ │ │ ├── PasswordUtil.java
│ │ │ │ └── ValidationUtil.java
│ │ │ │
│ │ │ └── event/ # 事件处理
│ │ │ ├── UserCreatedEvent.java
│ │ │ └── UserEventListener.java
│ │ │
│ │ └── resources/
│ │ ├── application.yml # 主配置文件
│ │ ├── application-dev.yml # 开发环境配置
│ │ ├── application-test.yml # 测试环境配置
│ │ ├── application-prod.yml # 生产环境配置
│ │ ├── db/migration/ # Flyway 数据库迁移脚本
│ │ │ ├── V1__init_schema.sql
│ │ │ ├── V2__add_user_profile.sql
│ │ │ └── V3__add_index.sql
│ │ ├── templates/ # 邮件模板
│ │ │ └── welcome-email.html
│ │ └── static/ # 静态资源
│ │
│ └── test/
│ ├── java/com/company/user/
│ │ ├── controller/ # 控制器测试
│ │ │ └── UserControllerTest.java
│ │ ├── service/ # 服务层测试
│ │ │ └── UserServiceTest.java
│ │ ├── repository/ # 数据访问层测试
│ │ │ └── UserRepositoryTest.java
│ │ ├── integration/ # 集成测试
│ │ │ └── UserIntegrationTest.java
│ │ └── util/ # 工具类测试
│ │ └── JwtUtilTest.java
│ └── resources/
│ ├── application-test.yml
│ └── data/ # 测试数据
│ ├── users.json
│ └── roles.json
│
├── 📂 scripts/ # 服务相关脚本
│ ├── run-local.sh
│ ├── run-docker.sh
│ └── db-migrate.sh
│
└── 📂 docs/ # 服务文档
├── api-docs.md
└── deployment-guide.md
4.2 Node.js/Express 项目结构
api-gateway/ # API 网关服务
├── 📄 README.md
├── 📄 package.json
├── 📄 tsconfig.json
├── 📄 Dockerfile
├── 📄 .env.example
│
├── 📂 src/
│ ├── index.ts # 入口文件
│ ├── app.ts # Express 应用
│ │
│ ├── routes/ # 路由定义
│ │ ├── index.ts
│ │ ├── user.routes.ts
│ │ ├── auth.routes.ts
│ │ └── health.routes.ts
│ │
│ ├── controllers/ # 控制器
│ │ ├── user.controller.ts
│ │ ├── auth.controller.ts
│ │ └── health.controller.ts
│ │
│ ├── services/ # 服务层
│ │ ├── user.service.ts
│ │ ├── auth.service.ts
│ │ └── proxy.service.ts
│ │
│ ├── middlewares/ # 中间件
│ │ ├── auth.middleware.ts
│ │ ├── rateLimit.middleware.ts
│ │ ├── cors.middleware.ts
│ │ ├── error.middleware.ts
│ │ └── requestLogger.middleware.ts
│ │
│ ├── models/ # 数据模型
│ │ ├── user.model.ts
│ │ └── token.model.ts
│ │
│ ├── utils/ # 工具函数
│ │ ├── logger.ts
│ │ ├── errorHandler.ts
│ │ ├── validation.ts
│ │ └── helpers.ts
│ │
│ ├── config/ # 配置
│ │ ├── index.ts
│ │ ├── database.ts
│ │ ├── redis.ts
│ │ └── swagger.ts
│ │
│ └── types/ # TypeScript 类型
│ ├── express.d.ts
│ ├── user.types.ts
│ └── api.types.ts
│
├── 📂 tests/
│ ├── unit/
│ │ ├── controllers/
│ │ ├── services/
│ │ └── middlewares/
│ ├── integration/
│ │ └── api.test.ts
│ └── e2e/
│ └── gateway.test.ts
│
├── 📂 migrations/ # 数据库迁移
│ └── 001_initial_schema.ts
│
└── 📂 scripts/
├── start.sh
├── test.sh
└── migrate.sh
5. 前端应用目录规划
5.1 React + TypeScript 项目结构
web-admin/ # 后台管理系统(React)
├── 📄 README.md
├── 📄 package.json
├── 📄 tsconfig.json
├── 📄 vite.config.ts
├── 📄 index.html
├── 📄 .env.local.example
│
├── 📂 public/
│ ├── favicon.ico
│ ├── manifest.json
│ └── locales/ # i18n 国际化文件
│ ├── zh-CN/translation.json
│ └── en-US/translation.json
│
├── 📂 src/
│ ├── main.tsx # 应用入口
│ ├── App.tsx # 根组件
│ │
│ ├── pages/ # 页面组件
│ │ ├── Dashboard/
│ │ │ ├── index.tsx
│ │ │ ├── Dashboard.tsx
│ │ │ └── components/
│ │ ├── UserManagement/
│ │ │ ├── index.tsx
│ │ │ ├── UserList.tsx
│ │ │ ├── UserForm.tsx
│ │ │ └── UserDetails.tsx
│ │ ├── OrderManagement/
│ │ └── Settings/
│ │
│ ├── components/ # 共享组件
│ │ ├── common/ # 通用组件
│ │ │ ├── Button/
│ │ │ │ ├── Button.tsx
│ │ │ │ ├── Button.styles.ts
│ │ │ │ └── index.ts
│ │ │ ├── Modal/
│ │ │ ├── Table/
│ │ │ └── Form/
│ │ ├── layout/ # 布局组件
│ │ │ ├── Header.tsx
│ │ │ ├── Sidebar.tsx
│ │ │ ├── Footer.tsx
│ │ │ └── MainLayout.tsx
│ │ └── business/ # 业务组件
│ │ ├── UserCard.tsx
│ │ ├── OrderStatus.tsx
│ │ └── PaymentForm.tsx
│ │
│ ├── hooks/ # 自定义 Hooks
│ │ ├── useAuth.ts
│ │ ├── useFetch.ts
│ │ ├── useForm.ts
│ │ └── usePagination.ts
│ │
│ ├── services/ # API 服务
│ │ ├── api.ts # Axios 实例配置
│ │ ├── user.api.ts
│ │ ├── order.api.ts
│ │ └── auth.api.ts
│ │
│ ├── store/ # 状态管理(Redux/Zustand)
│ │ ├── index.ts
│ │ ├── slices/
│ │ │ ├── authSlice.ts
│ │ │ ├── userSlice.ts
│ │ │ └── appSlice.ts
│ │ └── selectors/
│ │
│ ├── utils/ # 工具函数
│ │ ├── format.ts
│ │ ├── validation.ts
│ │ ├── constants.ts
│ │ └── helpers.ts
│ │
│ ├── styles/ # 全局样式
│ │ ├── variables.css
│ │ ├── global.css
│ │ └── mixins.scss
│ │
│ ├── types/ # TypeScript 类型
│ │ ├── user.types.ts
│ │ ├── order.types.ts
│ │ └── api.types.ts
│ │
│ ├── assets/ # 静态资源
│ │ ├── images/
│ │ ├── icons/
│ │ └── fonts/
│ │
│ └── config/ # 配置文件
│ ├── routes.tsx # 路由配置
│ ├── menu.tsx # 菜单配置
│ └── permissions.ts # 权限配置
│
├── 📂 tests/
│ ├── components/
│ │ └── Button.test.tsx
│ ├── pages/
│ │ └── Dashboard.test.tsx
│ ├── hooks/
│ │ └── useAuth.test.ts
│ └── utils/
│ └── format.test.ts
│
├── 📂 .storybook/ # Storybook 配置
│ ├── main.ts
│ └── preview.tsx
│
└── 📂 scripts/
├── build.sh
└── analyze.sh
5.2 Vue 3 + TypeScript 项目结构
web-mobile/ # 移动端 H5 应用(Vue 3)
├── 📄 README.md
├── 📄 package.json
├── 📄 tsconfig.json
├── 📄 vite.config.ts
├── 📄 index.html
│
├── 📂 public/
│ └── assets/
│
├── 📂 src/
│ ├── main.ts # 入口文件
│ ├── App.vue # 根组件
│ │
│ ├── views/ # 页面视图
│ │ ├── Home/
│ │ │ ├── index.vue
│ │ │ └── components/
│ │ ├── Product/
│ │ ├── Cart/
│ │ ├── Order/
│ │ └── Profile/
│ │
│ ├── components/ # 组件
│ │ ├── base/ # 基础组件
│ │ │ ├── BaseButton.vue
│ │ │ ├── BaseInput.vue
│ │ │ └── BaseModal.vue
│ │ ├── business/ # 业务组件
│ │ │ ├── ProductCard.vue
│ │ │ ├── CartItem.vue
│ │ │ └── OrderSummary.vue
│ │ └── layout/ # 布局组件
│ │ ├── NavBar.vue
│ │ ├── TabBar.vue
│ │ └── Container.vue
│ │
│ ├── composables/ # Composition API
│ │ ├── useAuth.ts
│ │ ├── useCart.ts
│ │ ├── useProduct.ts
│ │ └── useApi.ts
│ │
│ ├── router/ # 路由配置
│ │ ├── index.ts
│ │ └── routes.ts
│ │
│ ├── stores/ # Pinia 状态管理
│ │ ├── index.ts
│ │ ├── user.store.ts
│ │ ├── cart.store.ts
│ │ └── product.store.ts
│ │
│ ├── api/ # API 调用
│ │ ├── client.ts # Axios 实例
│ │ ├── user.api.ts
│ │ ├── product.api.ts
│ │ └── order.api.ts
│ │
│ ├── utils/ # 工具函数
│ │ ├── format.ts
│ │ ├── storage.ts
│ │ └── validators.ts
│ │
│ ├── styles/ # 样式
│ │ ├── variables.scss
│ │ ├── mixins.scss
│ │ └── global.scss
│ │
│ ├── types/ # 类型定义
│ │ ├── product.ts
│ │ ├── order.ts
│ │ └── user.ts
│ │
│ ├── assets/ # 资源文件
│ │ ├── images/
│ │ └── icons/
│ │
│ └── plugins/ # 插件
│ ├── vuetify.ts
│ └── i18n.ts
│
├── 📂 tests/
│ ├── unit/
│ │ ├── components/
│ │ └── composables/
│ └── e2e/
│ └── mobile-flow.spec.ts
│
└── 📂 scripts/
└── build-mobile.sh
6. 测试代码组织与管理
6.1 测试分层结构
tests/ # 测试总目录
├── 📂 unit/ # 单元测试
│ ├── backend/ # 后端单元测试
│ │ ├── user-service/
│ │ │ ├── controller/
│ │ │ ├── service/
│ │ │ ├── repository/
│ │ │ └── util/
│ │ └── order-service/
│ │
│ └── frontend/ # 前端单元测试
│ ├── components/
│ ├── hooks/
│ ├── utils/
│ └── store/
│
├── 📂 integration/ # 集成测试
│ ├── api/ # API 集成测试
│ │ ├── user-api.spec.ts
│ │ ├── order-api.spec.ts
│ │ └── auth-api.spec.ts
│ │
│ ├── database/ # 数据库集成测试
│ │ ├── user-repository.spec.ts
│ │ └── migration.spec.ts
│ │
│ └── message-queue/ # 消息队列集成测试
│ └── rabbitmq.spec.ts
│
├── 📂 e2e/ # 端到端测试
│ ├── specs/ # 测试规格
│ │ ├── login.spec.ts
│ │ ├── user-management.spec.ts
│ │ ├── order-flow.spec.ts
│ │ └── payment-flow.spec.ts
│ │
│ ├── fixtures/ # 测试夹具
│ │ ├── users.json
│ │ ├── products.json
│ │ └── orders.json
│ │
│ ├── pages/ # Page Objects
│ │ ├── LoginPage.ts
│ │ ├── DashboardPage.ts
│ │ └── OrderPage.ts
│ │
│ ├── utils/ # 测试工具
│ │ ├── test-helpers.ts
│ │ └── api-helpers.ts
│ │
│ └── playwright.config.ts # Playwright 配置
│
├── 📂 performance/ # 性能测试
│ ├── k6/ # k6 脚本
│ │ ├── load-test.js
│ │ ├── stress-test.js
│ │ └── soak-test.js
│ │
│ └── jmeter/ # JMeter 脚本
│ ├── api-load-test.jmx
│ └── database-benchmark.jmx
│
├── 📂 security/ # 安全测试
│ ├── penetration/ # 渗透测试
│ └── vulnerability/ # 漏洞扫描
│
└── 📂 shared/ # 共享测试资源
├── mocks/ # Mock 数据
│ ├── user.mock.ts
│ ├── order.mock.ts
│ └── api.mock.ts
│
├── factories/ # 测试数据工厂
│ ├── user.factory.ts
│ └── order.factory.ts
│
├── matchers/ # 自定义 Matchers
│ └── custom-matchers.ts
│
└── setup/ # 测试环境设置
├── jest.setup.ts
├── playwright.setup.ts
└── global.setup.ts
6.2 测试配置文件
# ===== Jest 配置(frontend/jest.config.js)=====
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
roots: ['<rootDir>/src', '<rootDir>/tests'],
testMatch: [
'**/__tests__/**/*.+(ts|tsx|js)',
'**/?(*.)+(spec|test).+(ts|tsx|js)'
],
transform: {
'^.+\\.(ts|tsx)$': 'ts-jest'
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'\\.css$': 'identity-obj-proxy'
},
setupFilesAfterEnv: ['<rootDir>/tests/shared/setup/jest.setup.ts'],
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!src/**/*.d.ts',
'!src/main.tsx'
],
coverageThreshold: {
global: {
branches: 70,
functions: 80,
lines: 80,
statements: 80
}
}
};
# ===== Playwright 配置(tests/e2e/playwright.config.ts)=====
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './specs',
timeout: 30 * 1000,
expect: {
timeout: 5000
},
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [
['html'],
['junit', { outputFile: 'results.xml' }]
],
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure'
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] }
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] }
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] }
},
{
name: 'Mobile Chrome',
use: { ...devices['Pixel 5'] }
}
],
webServer: {
command: 'npm run dev',
port: 3000,
reuseExistingServer: !process.env.CI
}
});
7. 部署脚本与配置文件
7.1 Shell 部署脚本示例
#!/bin/bash
# deploy/scripts/deploy-prod.sh - 生产环境部署脚本
set -euo pipefail
# 配置变量
ENV="prod"
NAMESPACE="production"
REGISTRY="harbor.yourcompany.com"
PROJECT="backend-services"
VERSION=${BUILD_NUMBER:-"latest"}
# 颜色输出
log_info() { echo -e "\033[0;32m[INFO]\033[0m $1"; }
log_warn() { echo -e "\033[0;33m[WARN]\033[0m $1"; }
log_error() { echo -e "\033[0;31m[ERROR]\033[0m $1"; }
# 前置检查
check_prerequisites() {
log_info "Checking prerequisites..."
if ! command -v kubectl &> /dev/null; then
log_error "kubectl not found"
exit 1
fi
if ! command -v helm &> /dev/null; then
log_error "helm not found"
exit 1
fi
if ! kubectl cluster-info &> /dev/null; then
log_error "Cannot connect to Kubernetes cluster"
exit 1
fi
log_info "Prerequisites check passed"
}
# 构建并推送镜像
build_and_push_images() {
log_info "Building and pushing Docker images..."
for service in user-service order-service payment-service; do
IMAGE_NAME="${REGISTRY}/${PROJECT}/${service}:${VERSION}"
log_info "Building ${service}..."
docker build -t ${IMAGE_NAME} services/${service}/
log_info "Pushing ${IMAGE_NAME}..."
docker push ${IMAGE_NAME}
done
log_info "All images built and pushed successfully"
}
# 更新 Kubernetes 配置
update_k8s_configs() {
log_info "Updating Kubernetes configurations..."
# 使用 Kustomize 更新镜像标签
cd deploy/kubernetes/overlays/${ENV}
kustomize edit set image \
user-service=${REGISTRY}/${PROJECT}/user-service:${VERSION} \
order-service=${REGISTRY}/${PROJECT}/order-service:${VERSION} \
payment-service=${REGISTRY}/${PROJECT}/payment-service:${VERSION}
log_info "Kubernetes configurations updated"
}
# 部署到 Kubernetes
deploy_to_k8s() {
log_info "Deploying to Kubernetes cluster..."
kubectl apply -k deploy/kubernetes/overlays/${ENV}/
log_info "Waiting for deployments to be ready..."
kubectl rollout status deployment/user-service -n ${NAMESPACE} --timeout=300s
kubectl rollout status deployment/order-service -n ${NAMESPACE} --timeout=300s
kubectl rollout status deployment/payment-service -n ${NAMESPACE} --timeout=300s
log_info "Deployment completed successfully"
}
# 健康检查
health_check() {
log_info "Running health checks..."
SERVICES=("user-service" "order-service" "payment-service")
for service in "${SERVICES[@]}"; do
ENDPOINT=$(kubectl get svc ${service} -n ${NAMESPACE} -o jsonpath={'.status.loadBalancer.ingress[0].ip'})
if curl -f http://${ENDPOINT}/actuator/health &> /dev/null; then
log_info "${service} is healthy"
else
log_error "${service} health check failed"
exit 1
fi
done
log_info "All health checks passed"
}
# 清理旧版本
cleanup_old_versions() {
log_info "Cleaning up old versions..."
# 保留最近 10 个版本
docker images ${REGISTRY}/${PROJECT}/* --format "{{.Repository}}:{{.Tag}}" | \
tail -n +11 | xargs -r docker rmi
log_info "Cleanup completed"
}
# 主流程
main() {
log_info "Starting production deployment (Version: ${VERSION})"
check_prerequisites
build_and_push_images
update_k8s_configs
deploy_to_k8s
health_check
cleanup_old_versions
log_info "🎉 Production deployment completed successfully!"
}
main "$@"
7.2 Jenkins Pipeline 脚本
pipeline {
agent any
options {
buildDiscarder(logRotator(numToKeepStr: '20'))
timeout(time: 2, unit: 'HOURS')
timestamps()
}
environment {
DOCKER_REGISTRY = 'harbor.yourcompany.com'
KUBECONFIG_CREDENTIALS_ID = 'kubeconfig-prod'
}
parameters {
choice(
name: 'DEPLOY_ENV',
choices: ['dev', 'staging', 'prod'],
description: '选择部署环境'
)
string(
name: 'VERSION',
defaultValue: '${BUILD_NUMBER}',
description: '部署版本号'
)
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build & Test') {
parallel {
stage('Build Backend') {
steps {
dir('services') {
sh 'mvn clean package -DskipTests'
}
}
}
stage('Build Frontend') {
steps {
dir('apps') {
sh 'npm ci && npm run build'
}
}
}
stage('Unit Tests') {
steps {
sh './scripts/ci/run-tests.sh'
}
post {
always {
junit allowEmptyResults: true, testResults: '**/target/surefire-reports/*.xml'
}
}
}
}
}
stage('Build Docker Images') {
steps {
script {
def services = ['user-service', 'order-service', 'payment-service']
services.each { service ->
def imageName = "${DOCKER_REGISTRY}/backend-services/${service}:${params.VERSION}"
sh """
docker build -t ${imageName} services/${service}/
docker push ${imageName}
"""
}
}
}
}
stage('Deploy to Kubernetes') {
steps {
withKubeConfig([credentialsId: KUBECONFIG_CREDENTIALS_ID]) {
sh """
./deploy/scripts/deploy-${params.DEPLOY_ENV}.sh --version ${params.VERSION}
"""
}
}
}
stage('Smoke Test') {
steps {
sh '''
curl -f http://api-gateway.production.svc.cluster.local/actuator/health
'''
}
}
}
post {
success {
echo '✅ Deployment completed successfully!'
slackSend(color: 'good', message: "Deployment to ${params.DEPLOY_ENV} succeeded: ${BUILD_URL}")
}
failure {
echo '❌ Deployment failed!'
slackSend(color: 'danger', message: "Deployment to ${params.DEPLOY_ENV} failed: ${BUILD_URL}")
}
}
}
8. Docker 与 K8s 配置组织
8.1 Docker Compose 配置
# deploy/docker/docker-compose.prod.yml
version: '3.8'
services:
api-gateway:
image: harbor.yourcompany.com/backend-services/api-gateway:latest
ports:
- "80:8080"
- "443:8443"
environment:
- SPRING_PROFILES_ACTIVE=prod
- LOG_LEVEL=INFO
depends_on:
- user-service
- order-service
networks:
- backend-network
restart: always
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
user-service:
image: harbor.yourcompany.com/backend-services/user-service:latest
environment:
- SPRING_PROFILES_ACTIVE=prod
- DB_HOST=postgres-primary
- REDIS_HOST=redis-master
depends_on:
- postgres-primary
- redis-master
networks:
- backend-network
restart: always
order-service:
image: harbor.yourcompany.com/backend-services/order-service:latest
environment:
- SPRING_PROFILES_ACTIVE=prod
- DB_HOST=postgres-primary
- KAFKA_BROKERS=kafka:9092
depends_on:
- postgres-primary
- kafka
networks:
- backend-network
restart: always
postgres-primary:
image: postgres:15-alpine
environment:
- POSTGRES_DB=appdb
- POSTGRES_USER=admin
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- backend-network
redis-master:
image: redis:7-alpine
volumes:
- redis-data:/data
networks:
- backend-network
kafka:
image: confluentinc/cp-kafka:7.4.0
environment:
- KAFKA_BROKER_ID=1
- KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181
- KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092
depends_on:
- zookeeper
networks:
- backend-network
networks:
backend-network:
driver: bridge
volumes:
postgres-data:
redis-data:
8.2 Kubernetes Kustomize 配置
# deploy/kubernetes/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- user-service-deployment.yaml
- user-service-service.yaml
- order-service-deployment.yaml
- order-service-service.yaml
- api-gateway-deployment.yaml
- api-gateway-service.yaml
- configmap.yaml
- secret.yaml
commonLabels:
app.kubernetes.io/managed-by: kustomize
configMapGenerator:
- name: app-config
literals:
- LOG_LEVEL=INFO
- METRICS_ENABLED=true
secretGenerator:
- name: db-credentials
literals:
- DB_USERNAME=admin
- DB_PASSWORD=changeme
---
# deploy/kubernetes/overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
namespace: production
namePrefix: prod-
replicas:
- name: user-service
count: 5
- name: order-service
count: 3
- name: api-gateway
count: 2
patchesStrategicMerge:
- resource-limits-patch.yaml
- hpa-patch.yaml
images:
- name: user-service
newName: harbor.yourcompany.com/backend-services/user-service
newTag: v1.2.3
- name: order-service
newName: harbor.yourcompany.com/backend-services/order-service
newTag: v1.2.3
configMapGenerator:
- name: app-config
behavior: merge
literals:
- LOG_LEVEL=WARN
- ENV=production
9. 文档与资源文件管理
9.1 文档组织结构
docs/
├── 📂 architecture/ # 架构文档
│ ├── system-overview.md # 系统概览
│ ├── architecture-decision-records/ # 架构决策记录(ADR)
│ │ ├── 001-use-monorepo.md
│ │ ├── 002-choose-postgresql.md
│ │ └── 003-implement-event-sourcing.md
│ ├── database/
│ │ ├── schema-design.md
│ │ ├── er-diagram.md
│ │ └── migration-strategy.md
│ └── api/
│ ├── api-design-principles.md
│ ├── rest-api-spec.md
│ └── graphql-schema.md
│
├── 📂 guides/ # 开发指南
│ ├── getting-started/
│ │ ├── prerequisites.md
│ │ ├── local-setup.md
│ │ └── first-contribution.md
│ ├── development/
│ │ ├── coding-standards.md
│ │ ├── git-workflow.md
│ │ ├── testing-guide.md
│ │ └── debugging-tips.md
│ └── deployment/
│ ├── dev-environment.md
│ ├── staging-deployment.md
│ └── production-deployment.md
│
├── 📂 runbook/ # 运维手册
│ ├── oncall-guide.md # 值班指南
│ ├── incident-response.md # 事故响应流程
│ ├── troubleshooting/
│ │ ├── common-issues.md
│ │ ├── performance-issues.md
│ │ └── database-issues.md
│ └── maintenance/
│ ├── scheduled-tasks.md
│ └── backup-restore.md
│
├── 📂 product/ # 产品文档
│ ├── requirements/
│ │ ├── prd-template.md
│ │ └── feature-requests.md
│ ├── user-guides/
│ │ ├── admin-guide.md
│ │ └── end-user-guide.md
│ └── release-notes/
│ ├── v1.0.0.md
│ └── v1.1.0.md
│
└── 📂 team/ # 团队文档
├── onboarding/
│ ├── new-hire-checklist.md
│ └── mentorship-program.md
├── processes/
│ ├── code-review-process.md
│ └── sprint-planning.md
└── knowledge-base/
├── best-practices.md
└── lessons-learned.md
9.2 架构决策记录(ADR)模板
# ADR-001: 采用 Monorepo 架构
## 状态
✅ 已采纳
## 背景
随着项目规模扩大,我们面临以下挑战:
- 多个仓库间代码共享困难
- 依赖版本不一致导致兼容性问题
- 跨服务重构成本高
- CI/CD 流程重复且分散
## 决策
采用 Monorepo(单仓库)架构,将所有服务、库、应用放在同一个 Git 仓库中。
### 选择的工具
- **Nx**: 用于构建缓存和受影响项目检测
- **pnpm workspaces**: 用于依赖管理
- **Turborepo**: 作为备选方案
## 后果
### 积极影响
✅ 代码共享变得简单直接
✅ 依赖版本统一管理
✅ 跨项目重构更容易
✅ CI/CD 可以优化(只构建受影响的项目)
✅ 新人上手更快(一个仓库包含所有)
### 消极影响
❌ 仓库体积会快速增长
❌ 需要更精细的权限控制
❌ CI/CD 配置复杂度增加
❌ 需要学习新工具(Nx/Turborepo)
### 缓解措施
- 实施代码所有权(CODEOWNERS)
- 配置增量构建和测试
- 定期清理无用文件和历史
- 提供详细的开发文档和培训
## 符合度
- ✅ 符合团队规模化发展需求
- ✅ 符合快速迭代的产品策略
- ⚠️ 需要投入初期学习和配置成本
## 参考链接
- [Nx 官方文档](https://nx.dev)
- [Turborepo 官方文档](https://turbo.build/repo)
- [Google Monorepo 论文](https://research.google/pubs/pub45424/)
## 变更日期
2026-03-12
## 变更人
张三(技术总监)
10. 最佳实践与工具推荐
📦 包管理工具
- pnpm:快速、节省磁盘空间(硬链接)
- Yarn Workspaces:成熟稳定
- NPM Workspaces:内置支持,无需额外安装
- Bun:新兴工具,速度极快
🔧 构建工具
- Vite:前端首选,极速 HMR
- Turbopack:Next.js 团队出品,潜力巨大
- Webpack 5:生态最丰富
- Rspack:Rust 编写,性能优异
🧪 测试框架
- Vitest:Vite 原生支持,速度快
- Jest:最流行,生态好
- Playwright:E2E 测试首选
- Cypress:开发者体验好
🚀 CI/CD 平台
- GitHub Actions:与 GitHub 深度集成
- GitLab CI:功能强大,自托管方便
- Jenkins:灵活,插件丰富
- ArgoCD:GitOps 首选
✅ 目录组织最佳实践总结:
- 一致性优先:全项目统一命名规范和目录结构
- 关注点分离:代码、测试、配置、文档清晰分开
- 约定优于配置:使用标准目录名减少配置
- 渐进式复杂:从小项目开始,随规模逐步演进
- 自动化辅助:用脚本和工具自动化日常操作
- 文档同步:目录变更及时更新文档
- 定期审查:每季度审查目录结构,清理冗余