|
| 1 | +# Object UI Packages 重构方案 |
| 2 | + |
| 3 | +## 当前问题分析 |
| 4 | + |
| 5 | +### 现有结构 |
| 6 | +``` |
| 7 | +packages/ |
| 8 | +├── core/ # ❌ 仅有占位代码 |
| 9 | +├── designer/ # ✅ 设计器(正确) |
| 10 | +├── engine/ # ❌ 仅有版本号占位 |
| 11 | +├── protocol/ # ⚠️ 定义了 SchemaNode 但职责不清 |
| 12 | +├── react/ # ❌ 仅有占位代码 |
| 13 | +├── renderer/ # ⚠️ 实际承担了 SchemaRenderer 和所有渲染器 |
| 14 | +└── ui/ # ⚠️ 包含了 Shadcn 组件和 metadata |
| 15 | +``` |
| 16 | + |
| 17 | +### 核心问题 |
| 18 | +1. **职责混乱**: `renderer` 包同时承担了注册表、渲染器和组件映射 |
| 19 | +2. **命名不符**: 没有 `components` 包,违反了 copilot-instructions 中的标准 |
| 20 | +3. **空包存在**: `core`、`engine`、`react` 几乎没有实际内容 |
| 21 | +4. **边界模糊**: `protocol` vs `core` vs `engine` 的分工不明确 |
| 22 | + |
| 23 | +--- |
| 24 | + |
| 25 | +## 标准架构(目标) |
| 26 | + |
| 27 | +根据 `.github/copilot-instructions.md` 和 `docs/spec/` 定义的标准架构: |
| 28 | + |
| 29 | +``` |
| 30 | +packages/ |
| 31 | +├── core/ # 🧠 The Brain - 纯 TypeScript 逻辑 |
| 32 | +│ ├── types/ # Schema 类型定义(从 protocol 迁移) |
| 33 | +│ ├── registry/ # 组件注册表(从 renderer 迁移) |
| 34 | +│ ├── data-scope/ # 数据作用域链 |
| 35 | +│ ├── evaluator/ # 表达式求值引擎 |
| 36 | +│ └── validators/ # Zod 验证器 |
| 37 | +│ |
| 38 | +├── react/ # 🔌 The Glue - React 绑定层 |
| 39 | +│ ├── SchemaRenderer.tsx # 主渲染器(从 renderer 迁移) |
| 40 | +│ ├── hooks/ # useRenderer, useDataScope |
| 41 | +│ └── context/ # React Context 包装器 |
| 42 | +│ |
| 43 | +├── components/ # 💪 The Body - 官方标准 UI 实现 |
| 44 | +│ ├── ui/ # Shadcn 原子组件(从 ui 迁移) |
| 45 | +│ └── renderers/ # ObjectUI 包装器(从 renderer 迁移) |
| 46 | +│ ├── basic/ # 基础组件渲染器 |
| 47 | +│ ├── form/ # 表单组件渲染器 |
| 48 | +│ ├── layout/ # 布局组件渲染器 |
| 49 | +│ └── ... |
| 50 | +│ |
| 51 | +├── designer/ # 🎨 The Tool - 可视化编辑器(保持不变) |
| 52 | +│ |
| 53 | +├── plugins/ # ⚔️ The Weapons - 重量级集成(新增) |
| 54 | +│ ├── ag-grid/ # AG Grid 集成 |
| 55 | +│ ├── devexpress/ # DevExpress 集成 |
| 56 | +│ └── monaco/ # Monaco 编辑器集成 |
| 57 | +│ |
| 58 | +└── [废弃包] |
| 59 | + ├── protocol/ # ❌ 合并到 core/types |
| 60 | + ├── engine/ # ❌ 合并到 core |
| 61 | + ├── renderer/ # ❌ 拆分到 react + components |
| 62 | + └── ui/ # ❌ 重命名为 components/ui |
| 63 | +``` |
| 64 | + |
| 65 | +--- |
| 66 | + |
| 67 | +## 详细迁移路径 |
| 68 | + |
| 69 | +### Phase 1: 创建新的标准结构 |
| 70 | + |
| 71 | +#### 1.1 重构 `packages/core` |
| 72 | + |
| 73 | +**目标**: 成为纯 TypeScript 的核心逻辑层,零 React 依赖 |
| 74 | + |
| 75 | +``` |
| 76 | +packages/core/ |
| 77 | +├── package.json # 依赖: zod, lodash (无 React) |
| 78 | +├── tsconfig.json |
| 79 | +└── src/ |
| 80 | + ├── index.ts |
| 81 | + ├── types/ |
| 82 | + │ ├── base.ts # BaseSchema interface |
| 83 | + │ ├── components.ts # 所有组件的 Schema 定义 |
| 84 | + │ ├── page.ts # PageSchema |
| 85 | + │ ├── view.ts # ViewSchema |
| 86 | + │ └── object.ts # ObjectSchema |
| 87 | + │ |
| 88 | + ├── registry/ |
| 89 | + │ ├── Registry.ts # 注册表核心逻辑 |
| 90 | + │ └── ComponentConfig.ts # 组件配置类型 |
| 91 | + │ |
| 92 | + ├── data-scope/ |
| 93 | + │ ├── DataScope.ts # 作用域链实现 |
| 94 | + │ └── ScopeChain.ts # 原型链查找 |
| 95 | + │ |
| 96 | + ├── evaluator/ |
| 97 | + │ ├── Evaluator.ts # 表达式求值 |
| 98 | + │ └── ExpressionParser.ts # ${...} 解析器 |
| 99 | + │ |
| 100 | + └── validators/ |
| 101 | + └── schemas.ts # Zod 验证规则 |
| 102 | +``` |
| 103 | + |
| 104 | +**迁移清单**: |
| 105 | +- ✅ 从 `packages/protocol/src/index.ts` 迁移 `SchemaNode` 等类型 |
| 106 | +- ✅ 从 `packages/renderer/src/registry.tsx` 迁移注册表逻辑 |
| 107 | +- ✅ 新建 `DataScope` 和 `Evaluator` 实现 |
| 108 | + |
| 109 | +--- |
| 110 | + |
| 111 | +#### 1.2 重构 `packages/react` |
| 112 | + |
| 113 | +**目标**: React 绑定层,连接 core 和 components |
| 114 | + |
| 115 | +``` |
| 116 | +packages/react/ |
| 117 | +├── package.json # peerDeps: react, react-dom |
| 118 | +│ # deps: @object-ui/core |
| 119 | +├── tsconfig.json |
| 120 | +└── src/ |
| 121 | + ├── index.ts |
| 122 | + ├── SchemaRenderer.tsx # 主渲染器(从 renderer 迁移) |
| 123 | + ├── hooks/ |
| 124 | + │ ├── useRenderer.ts |
| 125 | + │ ├── useDataScope.ts |
| 126 | + │ └── useRegistry.ts |
| 127 | + └── context/ |
| 128 | + ├── RendererContext.tsx |
| 129 | + └── DataScopeContext.tsx |
| 130 | +``` |
| 131 | + |
| 132 | +**迁移清单**: |
| 133 | +- ✅ 从 `packages/renderer/src/index.tsx` 迁移 `SchemaRenderer` |
| 134 | +- ✅ 创建 React Hooks 封装 core 逻辑 |
| 135 | + |
| 136 | +--- |
| 137 | + |
| 138 | +#### 1.3 创建 `packages/components` |
| 139 | + |
| 140 | +**目标**: 官方标准组件库,包含 Shadcn UI + ObjectUI 渲染器 |
| 141 | + |
| 142 | +``` |
| 143 | +packages/components/ |
| 144 | +├── package.json |
| 145 | +│ # deps: @object-ui/core, @object-ui/react |
| 146 | +│ # deps: @radix-ui/*, tailwindcss, lucide-react |
| 147 | +├── components.json # Shadcn 配置(从 ui 迁移) |
| 148 | +├── tailwind.config.js |
| 149 | +└── src/ |
| 150 | + ├── index.ts # 统一导出 |
| 151 | + ├── index.css # Tailwind 基础样式 |
| 152 | + │ |
| 153 | + ├── ui/ # 🟦 Shadcn 原子组件(从 packages/ui 迁移) |
| 154 | + │ ├── button.tsx |
| 155 | + │ ├── input.tsx |
| 156 | + │ ├── select.tsx |
| 157 | + │ └── ... |
| 158 | + │ |
| 159 | + └── renderers/ # 🟨 ObjectUI 包装器(从 renderer 迁移) |
| 160 | + ├── basic/ |
| 161 | + │ ├── ButtonRenderer.tsx |
| 162 | + │ ├── TextRenderer.tsx |
| 163 | + │ └── index.ts |
| 164 | + ├── form/ |
| 165 | + │ ├── InputRenderer.tsx |
| 166 | + │ ├── SelectRenderer.tsx |
| 167 | + │ └── index.ts |
| 168 | + ├── layout/ |
| 169 | + │ ├── ContainerRenderer.tsx |
| 170 | + │ ├── GridRenderer.tsx |
| 171 | + │ └── index.ts |
| 172 | + ├── data-display/ |
| 173 | + │ ├── TableRenderer.tsx |
| 174 | + │ └── CardRenderer.tsx |
| 175 | + └── index.ts # 自动注册所有渲染器 |
| 176 | +``` |
| 177 | + |
| 178 | +**迁移清单**: |
| 179 | +- ✅ 从 `packages/ui/src/components/ui/*` 迁移 Shadcn 组件 |
| 180 | +- ✅ 从 `packages/renderer/src/renderers/*` 迁移渲染器 |
| 181 | +- ✅ 保留 Tailwind 配置和样式 |
| 182 | + |
| 183 | +--- |
| 184 | + |
| 185 | +#### 1.4 创建 `packages/plugins/` |
| 186 | + |
| 187 | +**目标**: 隔离重量级第三方集成 |
| 188 | + |
| 189 | +``` |
| 190 | +packages/plugins/ |
| 191 | +├── ag-grid/ |
| 192 | +│ ├── package.json # deps: ag-grid-react |
| 193 | +│ └── src/ |
| 194 | +│ ├── GridRenderer.tsx |
| 195 | +│ └── index.ts |
| 196 | +│ |
| 197 | +├── devexpress/ |
| 198 | +│ ├── package.json # deps: devextreme-react |
| 199 | +│ └── src/ |
| 200 | +│ |
| 201 | +└── monaco/ |
| 202 | + ├── package.json # deps: @monaco-editor/react |
| 203 | + └── src/ |
| 204 | +``` |
| 205 | + |
| 206 | +--- |
| 207 | + |
| 208 | +### Phase 2: 更新依赖关系 |
| 209 | + |
| 210 | +#### 依赖链 |
| 211 | +``` |
| 212 | +designer |
| 213 | + ↓ |
| 214 | +components ──→ react ──→ core |
| 215 | + ↓ ↓ |
| 216 | +plugins/* (peerDeps: react, react-dom) |
| 217 | +``` |
| 218 | + |
| 219 | +#### Package.json 依赖矩阵 |
| 220 | + |
| 221 | +| Package | Dependencies | Peer Dependencies | |
| 222 | +|--------------|-------------------------------------------|-------------------| |
| 223 | +| `core` | zod, lodash | - | |
| 224 | +| `react` | @object-ui/core | react, react-dom | |
| 225 | +| `components` | @object-ui/core, @object-ui/react, @radix-ui/*, tailwindcss | react, react-dom | |
| 226 | +| `designer` | @object-ui/components | react, react-dom | |
| 227 | +| `plugins/*` | @object-ui/components, <plugin-lib> | react, react-dom | |
| 228 | + |
| 229 | +--- |
| 230 | + |
| 231 | +### Phase 3: 废弃旧包 |
| 232 | + |
| 233 | +```bash |
| 234 | +# 删除或归档 |
| 235 | +packages/protocol/ → 合并到 core/types |
| 236 | +packages/engine/ → 合并到 core |
| 237 | +packages/renderer/ → 拆分到 react + components |
| 238 | +packages/ui/ → 重命名为 components/ui |
| 239 | +``` |
| 240 | + |
| 241 | +--- |
| 242 | + |
| 243 | +## 迁移步骤(执行顺序) |
| 244 | + |
| 245 | +### Step 1: 准备阶段 |
| 246 | +```bash |
| 247 | +# 1. 创建新的包结构 |
| 248 | +mkdir -p packages/components/{src/{ui,renderers},metadata} |
| 249 | +mkdir -p packages/plugins/{ag-grid,devexpress,monaco} |
| 250 | + |
| 251 | +# 2. 备份当前代码 |
| 252 | +git checkout -b refactor/packages-restructure |
| 253 | +``` |
| 254 | + |
| 255 | +### Step 2: 迁移 Core(最底层) |
| 256 | +```bash |
| 257 | +# 从 protocol 迁移类型 |
| 258 | +cp packages/protocol/src/* packages/core/src/types/ |
| 259 | + |
| 260 | +# 从 renderer 迁移注册表 |
| 261 | +cp packages/renderer/src/registry.tsx packages/core/src/registry/Registry.ts |
| 262 | +# 🔧 移除 React 依赖,改为纯 TypeScript |
| 263 | +``` |
| 264 | + |
| 265 | +### Step 3: 迁移 React(中间层) |
| 266 | +```bash |
| 267 | +# 迁移 SchemaRenderer |
| 268 | +cp packages/renderer/src/index.tsx packages/react/src/SchemaRenderer.tsx |
| 269 | +# 🔧 更新 import 路径指向 @object-ui/core |
| 270 | +``` |
| 271 | + |
| 272 | +### Step 4: 迁移 Components(UI 层) |
| 273 | +```bash |
| 274 | +# 迁移 Shadcn UI 组件 |
| 275 | +cp -r packages/ui/src/components/ui packages/components/src/ui |
| 276 | +cp packages/ui/src/index.css packages/components/src/ |
| 277 | + |
| 278 | +# 迁移渲染器 |
| 279 | +cp -r packages/renderer/src/renderers packages/components/src/ |
| 280 | + |
| 281 | +# 迁移 metadata |
| 282 | +cp -r packages/ui/metadata packages/components/metadata |
| 283 | +``` |
| 284 | + |
| 285 | +### Step 5: 更新依赖 |
| 286 | +```bash |
| 287 | +# 在所有包中更新 import 路径 |
| 288 | +# 从: import { X } from '@object-ui/protocol' |
| 289 | +# 到: import { X } from '@object-ui/core' |
| 290 | + |
| 291 | +# 从: import { SchemaRenderer } from '@object-ui/renderer' |
| 292 | +# 到: import { SchemaRenderer } from '@object-ui/react' |
| 293 | +``` |
| 294 | + |
| 295 | +### Step 6: 删除旧包 |
| 296 | +```bash |
| 297 | +rm -rf packages/protocol |
| 298 | +rm -rf packages/engine |
| 299 | +rm -rf packages/renderer |
| 300 | +rm -rf packages/ui |
| 301 | +``` |
| 302 | + |
| 303 | +--- |
| 304 | + |
| 305 | +## 验证清单 |
| 306 | + |
| 307 | +### 功能验证 |
| 308 | +- [ ] `examples/prototype` 能正常构建和运行 |
| 309 | +- [ ] `examples/designer-demo` 能正常构建和运行 |
| 310 | +- [ ] 所有单元测试通过 |
| 311 | +- [ ] TypeScript 类型检查通过 |
| 312 | + |
| 313 | +### 架构验证 |
| 314 | +- [ ] `packages/core` 无 React 依赖 |
| 315 | +- [ ] `packages/react` 只依赖 core + React |
| 316 | +- [ ] `packages/components` 包含完整的 UI + 渲染器 |
| 317 | +- [ ] 循环依赖检查通过 |
| 318 | + |
| 319 | +### 文档验证 |
| 320 | +- [ ] 更新 `README.md` |
| 321 | +- [ ] 更新 `docs/guide/installation.md` |
| 322 | +- [ ] 更新 `docs/spec/project-structure.md` |
| 323 | +- [ ] 更新 `.github/copilot-instructions.md`(如需调整) |
| 324 | + |
| 325 | +--- |
| 326 | + |
| 327 | +## 关键设计决策 |
| 328 | + |
| 329 | +### 为什么合并 protocol 到 core? |
| 330 | +- `protocol` 原本只有类型定义,职责过窄 |
| 331 | +- 类型定义是核心逻辑的一部分,应该在 `core` 中 |
| 332 | +- 避免过度拆分导致包数量膨胀 |
| 333 | + |
| 334 | +### 为什么拆分 renderer? |
| 335 | +- `renderer` 违反了单一职责原则,同时包含: |
| 336 | + - 注册表逻辑(应该在 core) |
| 337 | + - React 绑定(应该在 react) |
| 338 | + - UI 组件(应该在 components) |
| 339 | +- 拆分后各层职责清晰,符合标准架构 |
| 340 | + |
| 341 | +### 为什么重命名 ui 为 components? |
| 342 | +- `ui` 命名过于宽泛 |
| 343 | +- `components` 明确表达「官方标准组件库」的定位 |
| 344 | +- 与 copilot-instructions 中的术语一致 |
| 345 | + |
| 346 | +### 为什么新增 plugins? |
| 347 | +- 隔离重量级第三方库(AG Grid 200KB+) |
| 348 | +- 支持按需加载,避免污染核心包 |
| 349 | +- 为未来扩展预留空间 |
| 350 | + |
| 351 | +--- |
| 352 | + |
| 353 | +## 风险评估 |
| 354 | + |
| 355 | +### 高风险项 |
| 356 | +1. **Import 路径大规模变更** - 可能遗漏部分引用 |
| 357 | + - 缓解: 使用 IDE 全局搜索替换 + TypeScript 检查 |
| 358 | + |
| 359 | +2. **循环依赖** - components 依赖 react,react 依赖 core |
| 360 | + - 缓解: 严格遵守单向依赖原则,禁止反向引用 |
| 361 | + |
| 362 | +### 中风险项 |
| 363 | +1. **测试覆盖不足** - 当前测试较少,可能遗漏回归问题 |
| 364 | + - 缓解: 重构前补充关键路径的集成测试 |
| 365 | + |
| 366 | +2. **第三方依赖版本冲突** - Shadcn 组件依赖特定 Radix 版本 |
| 367 | + - 缓解: 使用 pnpm 的 workspace 协议锁定版本 |
| 368 | + |
| 369 | +--- |
| 370 | + |
| 371 | +## 成功标准 |
| 372 | + |
| 373 | +重构完成后应达到: |
| 374 | + |
| 375 | +1. ✅ **架构清晰**: 每个包职责单一明确 |
| 376 | +2. ✅ **零 React 污染**: core 包可以在 Node.js 中使用 |
| 377 | +3. ✅ **Tree-shakable**: 用户只引入需要的组件 |
| 378 | +4. ✅ **向后兼容**: 旧代码通过 import 路径调整即可迁移 |
| 379 | +5. ✅ **文档完整**: AI 和人类都能快速理解架构 |
| 380 | + |
| 381 | +--- |
| 382 | + |
| 383 | +## 附录:参考文档 |
| 384 | + |
| 385 | +- [Architecture Blueprint](docs/spec/architecture.md) |
| 386 | +- [Project Structure Spec](docs/spec/project-structure.md) |
| 387 | +- [Copilot Instructions](.github/copilot-instructions.md) |
0 commit comments