Skip to Content

项目结构指南

掌握 Entity Engine 项目的最佳组织方式,构建可维护、可扩展的企业级应用架构。

为什么结构很重要

良好的项目结构能够:

  • 提升开发效率 - 团队成员能快速找到和修改代码
  • 降低维护成本 - 清晰的职责分离让代码更易理解和修改
  • 支持项目扩展 - 模块化设计让功能扩展更加容易
  • 促进团队协作 - 标准化的结构减少沟通成本

核心设计原则

Entity Engine 的项目结构遵循以下原则:

  • 关注点分离 - 数据模型、视图配置、业务逻辑分别管理
  • 约定优于配置 - 遵循约定的目录结构减少配置工作
  • 模块化组织 - 支持从小型项目到大型企业应用的渐进式发展

基础项目结构

适合中小型项目的标准组织方式:

my-entity-app/ ├── lib/ # 核心业务逻辑 │ ├── models.ts # 📊 数据模型定义 │ ├── views.ts # 🎨 UI 视图配置 │ └── entity-provider.tsx # ⚙️ 客户端配置 ├── app/ # Next.js 应用目录 │ ├── api/ee/[[...slug]]/ │ │ └── route.ts # 🔌 API 处理器 │ ├── products/ │ │ └── page.tsx # 📄 业务页面 │ └── layout.tsx # 🖼️ 应用布局 ├── prisma/ │ └── schema.prisma # 🗄️ 数据库结构 └── .env.local # 🔐 环境配置

核心文件详解

模型定义 (lib/models.ts)

集中管理所有数据模型:

lib/models.ts
import type { IEntityModel } from '@scenemesh/entity-engine'; export const UserModel: IEntityModel = { name: 'User', title: '用户', fields: [ { name: 'id', title: 'ID', type: 'string', isPrimaryKey: true, }, { name: 'name', title: '姓名', type: 'string', isRequired: true, searchable: true, }, { name: 'email', title: '邮箱', type: 'string', isRequired: true, isUnique: true, } ] }; export const ProductModel: IEntityModel = { name: 'Product', title: '产品', fields: [ { name: 'id', title: 'ID', type: 'string', isPrimaryKey: true, }, { name: 'name', title: '产品名称', type: 'string', isRequired: true, searchable: true, }, { name: 'price', title: '价格', type: 'number', isRequired: true, }, { name: 'user', title: '创建者', type: 'many_to_one', refModel: 'User', } ] }; // 导出所有模型 export const models = [UserModel, ProductModel];

视图配置 (lib/views.ts)

定义用户界面布局:

lib/views.ts
import type { IEntityView } from '@scenemesh/entity-engine'; export const UserGridView: IEntityView = { name: 'user_grid', title: '用户列表', modelName: 'User', viewType: 'grid', items: [ { type: 'field', name: 'name', spanCols: 4 }, { type: 'field', name: 'email', spanCols: 8 }, ] }; export const UserFormView: IEntityView = { name: 'user_form', title: '用户表单', modelName: 'User', viewType: 'form', items: [ { type: 'field', name: 'name' }, { type: 'field', name: 'email' }, ] }; export const ProductGridView: IEntityView = { name: 'product_grid', title: '产品列表', modelName: 'Product', viewType: 'grid', items: [ { type: 'field', name: 'name', spanCols: 4 }, { type: 'field', name: 'price', spanCols: 2 }, { type: 'field', name: 'user', spanCols: 6 }, ] }; export const ProductFormView: IEntityView = { name: 'product_form', title: '产品表单', modelName: 'Product', viewType: 'form', items: [ { type: 'field', name: 'name' }, { type: 'field', name: 'price' }, { type: 'field', name: 'user' }, ] }; // 导出所有视图 export const views = [ UserGridView, UserFormView, ProductGridView, ProductFormView, ];

客户端 Provider (lib/entity-provider.tsx)

配置客户端 Entity Engine:

lib/entity-provider.tsx
'use client'; import { createEntityEngineProvider } from '@scenemesh/entity-engine'; import { models } from './models'; import { views } from './views'; export const EntityEngineProvider = createEntityEngineProvider({ config: { models, views }, settings: { baseUrl: process.env.NEXT_PUBLIC_API_BASE_URL || '', endpoint: '/api/ee', authenticationEnabled: false, }, router: { navigate: (path: string) => { if (typeof window !== 'undefined') { window.location.href = path; } } }, permissionGuard: { checkPermission: async () => true, }, });

API 路由 (app/api/ee/[[...slug]]/route.ts)

服务端 API 处理器:

app/api/ee/[[...slug]]/route.ts
import { EnginePrimitiveInitializer, fetchEntityEntranceHandler } from '@scenemesh/entity-engine/server'; import { models } from '../../../../lib/models'; import { views } from '../../../../lib/views'; const initializer = new EnginePrimitiveInitializer({ models, views, }); const handler = (req: Request) => fetchEntityEntranceHandler({ request: req, endpoint: '/api/ee', initializer }); export { handler as GET, handler as POST };

大型项目结构

对于复杂应用,推荐使用模块化结构:

src/ ├── entities/ │ ├── user/ │ │ ├── models.ts │ │ ├── views.ts │ │ └── components.tsx │ ├── product/ │ │ ├── models.ts │ │ ├── views.ts │ │ └── components.tsx │ └── order/ │ ├── models.ts │ ├── views.ts │ └── components.tsx ├── lib/ │ ├── entity-registry.ts # 统一注册 │ └── entity-provider.tsx └── app/ ├── api/ee/[[...slug]]/route.ts └── (business modules)/

模块化注册 (lib/entity-registry.ts)

统一管理所有实体:

lib/entity-registry.ts
import { models as userModels, views as userViews } from '../entities/user'; import { models as productModels, views as productViews } from '../entities/product'; import { models as orderModels, views as orderViews } from '../entities/order'; export const allModels = [ ...userModels, ...productModels, ...orderModels, ]; export const allViews = [ ...userViews, ...productViews, ...orderViews, ];

单个实体模块 (entities/user/models.ts)

entities/user/models.ts
import type { IEntityModel } from '@scenemesh/entity-engine'; import { z } from 'zod'; export const UserModel: IEntityModel = { name: 'User', title: '用户', fields: [ { name: 'id', title: 'ID', type: 'string', isPrimaryKey: true, }, { name: 'name', title: '姓名', type: 'string', isRequired: true, searchable: true, schema: z.string().min(2, '姓名至少2个字符').max(50, '姓名最多50个字符'), }, { name: 'email', title: '邮箱', type: 'string', isRequired: true, isUnique: true, schema: z.string().email('请输入有效的邮箱地址'), }, { name: 'role', title: '角色', type: 'enum', defaultValue: 'user', typeOptions: { options: [ { value: 'admin', label: '管理员' }, { value: 'user', label: '普通用户' }, ] } } ] }; export const models = [UserModel];

环境配置

环境变量 (.env.local)

.env.local
# Entity Engine 数据库 EE_DATABASE_URL="postgresql://user:password@localhost:5432/myapp?schema=public" # API 配置 NEXT_PUBLIC_API_BASE_URL="" NEXT_PUBLIC_API_ENDPOINT="/api/ee" # 可选:开发模式配置 NODE_ENV="development" ENTITY_ENGINE_DEBUG="true"

TypeScript 配置 (tsconfig.json)

tsconfig.json
{ "compilerOptions": { "target": "ES2022", "lib": ["dom", "dom.iterable", "es6"], "allowJs": true, "skipLibCheck": true, "strict": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "incremental": true, "plugins": [ { "name": "next" } ], "baseUrl": ".", "paths": { "@/*": ["./src/*"], "@/lib/*": ["./lib/*"], "@/entities/*": ["./entities/*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] }

最佳实践

命名约定

  • 模型名称: 使用 PascalCase (UserModel, ProductModel)
  • 视图名称: 使用 snake_case (user_grid, product_form)
  • 文件名: 使用 kebab-case (user-profile.tsx, order-history.tsx)

代码组织

  • 关注点分离: 模型、视图、组件分别管理
  • 单一职责: 每个文件只负责一个实体或功能
  • 导入顺序: 第三方库 → 本地库 → 相对导入

性能优化

  • 延迟加载: 大型模块使用动态导入
  • 缓存策略: 合理使用 React 缓存机制
  • 数据分页: 大数据集启用分页查询

部署结构

生产环境目录

production/ ├── .next/ # Next.js 构建输出 ├── prisma/ │ ├── schema.prisma │ └── migrations/ ├── public/ ├── package.json └── .env.production # 生产环境变量

Docker 配置示例

Dockerfile
FROM node:18-alpine WORKDIR /app # 复制依赖文件 COPY package*.json ./ COPY prisma ./prisma/ # 安装依赖 RUN npm ci --only=production # 复制源代码 COPY . . # 构建应用 RUN npm run build # 生成 Prisma Client RUN npx prisma generate EXPOSE 3000 CMD ["npm", "start"]

下一步

Last updated on