# Monorepo架构技术设计文档 ## 架构概览 本设计文档详细描述了DeotalandAi项目从分散的独立项目向Monorepo架构迁移的技术实施方案。 ## 技术选型分析 ### 包管理器选择: pnpm vs Yarn vs npm #### 选择: pnpm **原因:** - **磁盘空间优化**: 硬链接和符号链接减少80%磁盘占用 - **速度快**: 并行安装,比npm快2-3倍 - **工作空间支持**: 原生Monorepo支持,配置简洁 - **依赖隔离**: 每个包独立node_modules,避免依赖污染 - **严格模式**: 防止"吊死依赖"问题 #### 对比分析: ``` pnpm: ✅ 安装速度快 ✅ 磁盘占用少 ✅ 工作空间原生支持 ✅ 依赖严格模式 ❌ 相对较新,生态还在完善 Yarn Workspaces: ✅ 成熟稳定 ✅ 社区支持好 ❌ 安装速度中等 ❌ 磁盘占用大 npm Workspaces: ✅ Node.js原生支持 ❌ 功能相对简单 ❌ 性能不如pnpm ``` ### 构建工具选择: Turborepo vs Lerna vs Nx #### 选择: Turborepo **原因:** - **任务管道**: 声明式任务依赖图,易于理解 - **缓存机制**: 本地和远程缓存,显著提升构建速度 - **增量构建**: 只重新构建变更的包 - **简单配置**: JSON配置,学习成本低 - **Vercel支持**: 来自Vercel,现代化工具链 #### 任务管道设计: ```json { "pipeline": { "build": { "dependsOn": ["^build"], "outputs": ["dist/**", "build/**"] }, "test": { "dependsOn": ["build"], "outputs": [] }, "lint": { "outputs": [] }, "dev": { "cache": false, "persistent": true } } } ``` ## 项目结构设计 ### 目录结构详解 ``` DeotalandAi/ ├── package.json # 根级别项目配置 ├── pnpm-workspace.yaml # pnpm工作空间定义 ├── turbo.json # Turborepo任务配置 ├── .npmrc # npm配置(使用pnpm时) ├── .eslintrc.base.json # 基础ESLint配置 ├── .prettierrc # Prettier配置 ├── .prettierrc # Prettier配置 ├── .gitignore # Git忽略规则 ├── README.md # 项目总体说明 ├── CHANGELOG.md # 版本变更记录 ├── apps/ # 应用程序 │ ├── frontend/ # 主前端应用 │ │ ├── package.json │ │ ├── vite.config.js │ │ ├── index.html │ │ └── src/ │ │ ├── main.ts │ │ ├── App.vue │ │ ├── components/ │ │ ├── views/ │ │ ├── stores/ │ │ └── router/ │ └── designer/ # 设计器应用 │ ├── package.json │ │ ├── vite.config.js │ │ ├── index.html │ │ └── src/ ├── packages/ # 共享包 │ ├── ui/ # UI组件库 │ │ ├── package.json │ │ ├── vite.config.js │ │ ├── src/ │ │ │ ├── index.js # 统一导出 │ │ │ ├── components/ # Vue组件 │ │ │ ├── styles/ # 样式文件 │ │ │ └── composables/ # 组合式函数 │ │ └── stories/ # Storybook文档 │ ├── utils/ # 工具库 │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── api/ # API相关工具 │ │ │ ├── format/ # 格式化工具 │ │ │ ├── validate/ # 验证工具 │ │ │ └── storage/ # 存储工具 │ │ └── tests/ │ └── config/ # 配置文件包 │ ├── package.json │ ├── src/ │ │ ├── index.js │ │ ├── vite.js # Vite配置 │ │ └── eslint.js # ESLint配置 ├── scripts/ # 构建和部署脚本 │ ├── build.js │ ├── dev.js │ ├── deploy.js │ └── utils.js └── docs/ # 项目文档 ├── architecture.md ├── contributing.md └── guides/ ``` ### 工作空间配置详解 #### pnpm-workspace.yaml ```yaml packages: - 'apps/*' - 'packages/*' - '!apps/*/dist/**' - '!apps/*/.vite/**' ``` #### package.json (根级别) ```json { "name": "deotalandai-monorepo", "private": true, "version": "1.0.0", "description": "DeotalandAI Monorepo with Vue3, Element Plus, and shared components", "scripts": { "dev": "turbo run dev", "build": "turbo run build", "lint": "turbo run lint", "test": "turbo run test", "clean": "turbo run clean && rimraf node_modules", "type-check": "turbo run type-check", "storybook": "turbo run storybook", "deploy": "turbo run deploy" }, "devDependencies": { "turbo": "^1.10.0", "concurrently": "^8.2.0", "rimraf": "^5.0.0", "eslint": "^8.45.0", "prettier": "^3.0.0", "@vitejs/plugin-vue": "^6.0.1", "vite": "^7.2.2" }, "engines": { "node": ">=18.0.0", "pnpm": ">=8.0.0" } } ``` ## 共享组件设计 ### UI组件库架构 #### 组件分类体系 ``` packages/ui/src/components/ ├── base/ # 基础组件 │ ├── Button/ │ ├── Input/ │ ├── Card/ │ ├── Modal/ │ └── Loading/ ├── layout/ # 布局组件 │ ├── Header/ │ ├── Sidebar/ │ ├── Footer/ │ └── Container/ ├── business/ # 业务组件 │ ├── AgentCard/ │ ├── OrderCard/ │ ├── Gallery/ │ └── PurchaseModal/ └── form/ # 表单组件 ├── Form/ ├── FormItem/ ├── Upload/ └── Select/ ``` #### 主题系统设计 ```typescript // packages/ui/src/styles/theme.ts export const theme = { colors: { primary: '#6B46C1', secondary: '#8B5CF6', success: '#10B981', warning: '#F59E0B', error: '#EF4444', info: '#3B82F6' }, spacing: { xs: '4px', sm: '8px', md: '16px', lg: '24px', xl: '32px' }, borderRadius: { sm: '4px', md: '8px', lg: '12px', full: '9999px' }, typography: { fontFamily: { sans: ['Inter', 'system-ui', 'sans-serif'], mono: ['JetBrains Mono', 'monospace'] }, fontSize: { xs: '12px', sm: '14px', md: '16px', lg: '18px', xl: '20px', '2xl': '24px' } } } ``` #### 组件使用示例 ```vue ``` ### 工具库设计 #### API工具模块 ```javascript // packages/utils/src/api/request.js import axios from 'axios' export class ApiClient { constructor(config) { this.instance = axios.create(config) this.setupInterceptors() } setupInterceptors() { this.instance.interceptors.request.use( (config) => { // 添加认证token const token = localStorage.getItem('auth_token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }, (error) => Promise.reject(error) ) this.instance.interceptors.response.use( (response) => response.data, (error) => { // 统一错误处理 return Promise.reject(this.handleError(error)) } ) } handleError(error) { if (error.response) { const { status, data } = error.response return { status, message: data.message || '请求失败', code: data.code } } return { status: 0, message: error.message || '网络错误', code: -1 } } async get(url, params) { return this.instance.get(url, { params }) } async post(url, data) { return this.instance.post(url, data) } async put(url, data) { return this.instance.put(url, data) } async delete(url) { return this.instance.delete(url) } } export const createApiClient = (baseURL) => { return new ApiClient({ baseURL }) } ``` #### 国际化工具 ```typescript // packages/utils/src/i18n/index.ts import { createI18n } from 'vue-i18n' import type { App } from 'vue' export const setupI18n = (app: App, locale: string, messages: Record) => { const i18n = createI18n({ legacy: false, locale, fallbackLocale: 'en', messages }) app.use(i18n) return i18n } export const formatDate = (date: Date, locale: string = 'zh-CN') => { return new Intl.DateTimeFormat(locale, { year: 'numeric', month: 'long', day: 'numeric' }).format(date) } export const formatCurrency = (amount: number, currency: string = 'CNY', locale: string = 'zh-CN') => { return new Intl.NumberFormat(locale, { style: 'currency', currency }).format(amount) } ``` ### 类型定义设计 #### API类型定义 ```typescript // packages/types/src/api.ts export interface ApiResponse { code: number message: string data: T success: boolean } export interface PaginatedResponse extends ApiResponse { pagination: { page: number pageSize: number total: number totalPages: number } } export interface Agent { id: string name: string description: string avatar: string category: string tags: string[] created_at: string updatedAt: string } export interface Order { id: string agentId: string userId: string status: 'pending' | 'processing' | 'completed' | 'failed' amount: number currency: string created_at: string updatedAt: string } ``` #### 组件Props类型 ```typescript // packages/types/src/components.ts import type { Agent, Order } from './api' export interface ButtonProps { variant?: 'primary' | 'secondary' | 'outline' | 'ghost' size?: 'sm' | 'md' | 'lg' disabled?: boolean loading?: boolean block?: boolean } export interface CardProps { shadow?: 'none' | 'sm' | 'md' | 'lg' border?: boolean padding?: 'none' | 'sm' | 'md' | 'lg' } export interface AgentCardProps { agent: Agent selectable?: boolean showActions?: boolean } export interface ModalProps { visible: boolean title?: string width?: string | number confirmText?: string cancelText?: string maskClosable?: boolean } ``` ## 开发工具配置 ### VS Code工作区配置 ```json // .vscode/settings.json { "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, "typescript.preferences.importModuleSpecifier": "relative", "emmet.includeLanguages": { "vue": "html" }, "files.associations": { "*.vue": "vue" }, "search.exclude": { "**/node_modules": true, "**/dist": true, "**/build": true, "**/.turbo": true } } ``` ### 调试配置 ```json // .vscode/launch.json { "version": "0.2.0", "configurations": [ { "name": "Frontend Dev", "type": "node", "request": "launch", "program": "${workspaceFolder}/node_modules/.bin/vite", "args": ["--config", "apps/frontend/vite.config.ts"], "cwd": "${workspaceFolder}/apps/frontend", "console": "integratedTerminal" } ] } ``` ## 性能优化策略 ### 构建优化 1. **依赖预构建**: 使用vite的依赖预构建 2. **代码分割**: 每个应用独立打包 3. **Tree Shaking**: 消除未使用的代码 4. **缓存策略**: 利用Turborepo缓存 ### 开发优化 1. **热模块替换**: 跨包的热更新 2. **增量构建**: 只构建变更的包 3. **并行开发**: 同时启动多个应用 4. **类型检查**: 增量TypeScript检查 ### 部署优化 1. **CDN分发**: 静态资源CDN部署 2. **压缩优化**: Gzip/Brotli压缩 3. **缓存头**: 合理的缓存策略 4. **域名分片**: 多个域名提升加载速度 ## 迁移风险与缓解 ### 技术风险 1. **构建复杂度**: 复杂的工作空间依赖图 - **缓解**: 使用Turborepo简化配置 2. **依赖冲突**: 不同包的依赖版本冲突 - **缓解**: 使用pnpm的严格模式 3. **构建时间**: 大型Monorepo构建时间长 - **缓解**: 利用Turborepo缓存和增量构建 ### 团队风险 1. **学习成本**: 团队需要学习新的工作方式 - **缓解**: 提供详细的文档和培训 2. **工具兼容性**: 现有工具可能不支持Monorepo - **缓解**: 选择兼容性好的工具 3. **CI/CD复杂性**: 流水线配置更复杂 - **缓解**: 提供完整的CI/CD模板 --- **设计版本**: 1.0 **创建时间**: 2025-11-18 **技术负责人**: **审核状态**: 待审核