Skip to content

Schema设计

本文档引用的文件

目录

  1. Schema设计
  2. Schema First方法实现原理
  3. 实体类型定义
  4. 输入类型与枚举类型
  5. 查询与变更操作设计
  6. 嵌套类型与关系字段处理
  7. 模式合并机制
  8. 最佳实践建议

Schema First方法实现原理

本项目采用Schema First的GraphQL设计方法,即先定义GraphQL Schema,再生成对应的TypeScript代码。通过codegen工具链,从数据库表结构自动生成.graphql.ts文件,再由这些文件生成resolver和model。

该方法的核心优势在于:

  • 强类型约束:Schema定义了API的契约,确保前后端一致
  • 自动生成代码:减少手动编写重复代码的工作量
  • 文档即代码:Schema本身就是最准确的API文档
  • 易于维护:修改Schema后可一键重新生成相关代码

项目中的codegen模块负责执行代码生成任务,通过读取数据库信息生成基础的GraphQL类型定义。

本节来源

实体类型定义

实体类型(Object Type)是GraphQL Schema中最基本的构建块,用于描述数据的结构。在本项目中,每个业务实体都有对应的.model.ts.graphql.ts文件。

usr(用户)实体为例,其类型定义包含以下字段:

  • id:唯一标识符
  • username:用户名
  • email:邮箱
  • status:状态
  • createdAt:创建时间
  • updatedAt:更新时间

实体类型通过TypeGraphQL装饰器与TypeScript类关联,实现类型安全的开发体验。

图示来源

本节来源

输入类型与枚举类型

输入类型(Input Type)

输入类型用于定义创建或更新操作所需的参数结构。与实体类型不同,输入类型通常不包含ID和时间戳字段。

例如,创建用户的输入类型定义如下:

typescript
@InputType()
class CreateUserInput {
  @Field()
  username: string;
  
  @Field()
  email: string;
  
  @Field({ nullable: true })
  deptId?: string;
}

枚举类型(Enum Type)

枚举类型用于限制字段的取值范围。在本项目中,状态字段普遍使用枚举类型。

图示来源

本节来源

查询与变更操作设计

查询(Query)设计模式

查询操作遵循标准化的分页、过滤和排序参数设计:

typescript
@ArgsType()
class FindUsersArgs {
  @Field({ defaultValue: 1 })
  page: number;
  
  @Field({ defaultValue: 10 })
  limit: number;
  
  @Field({ nullable: true })
  keyword?: string;
  
  @Field(() => [SortInput], { nullable: true })
  sort?: SortInput[];
}

变更(Mutation)操作

变更操作包括创建、更新和删除三种基本类型:

图示来源

本节来源

查询过滤与搜索功能更新

根据最新的代码变更,查询操作的输入类型已扩展了新的过滤和搜索功能:

菜单查询的租户过滤

MenuSearch输入类型中新增了is_current_tenant字段,用于过滤当前租户的数据:

graphql
input MenuSearch {
  "已删除"
  is_deleted: Int
  "仅当前租户"
  is_current_tenant: Int
  # 其他字段...
}

此功能允许在查询菜单时指定是否只返回当前租户的数据,增强了多租户环境下的数据隔离能力。

字典查询的关键字搜索

DictSearch输入类型中新增了keyword字段,支持通过关键字进行全文搜索:

graphql
input DictSearch {
  "关键字"
  keyword: String
  # 其他字段...
}

此功能允许用户通过单一关键字搜索字典的多个字段(如编码、名称等),提高了搜索的便利性和用户体验。

本节来源

嵌套类型与关系字段处理

GraphQL的强大之处在于能够通过嵌套查询获取关联数据。本项目通过以下方式处理关系字段:

  1. 一对一关系:直接在实体类型中包含关联对象
  2. 一对多关系:使用数组类型包含多个关联对象
  3. 多对多关系:通过中间表实现

以部门与用户的关系为例:

typescript
@ObjectType()
class Dept {
  @Field()
  id: string;
  
  @Field(() => [User])
  users: User[];
}

图示来源

本节来源

模式合并机制

本项目采用模块化的Schema设计,将不同业务模块的Schema分别定义,最后通过合并机制组成完整的API接口。

合并过程如下:

  1. 每个模块生成独立的.graphql.ts文件
  2. 在根resolver中导入所有模块的resolver
  3. 使用@Resolver装饰器注册所有查询和变更
  4. 启动时自动合并所有Schema
typescript
// graphql.ts
import * as UserResolvers from './base/usr/usr.resolver';
import * as DeptResolvers from './base/dept/dept.resolver';

const resolvers = [
  ...Object.values(UserResolvers),
  ...Object.values(DeptResolvers),
];

本节来源

最佳实践建议

命名约定

  • 实体类型:PascalCase,如User
  • 输入类型:实体名+Input,如CreateUserInput
  • 枚举类型:实体名+Enum,如UserStatusEnum
  • 查询方法:动词+实体名,如findUsers
  • 变更方法:动词+实体名,如createUser

字段描述

所有字段都应添加描述,便于生成文档:

typescript
@Field(() => String, { description: '用户邮箱地址' })
email: string;

版本控制策略

  • 使用Git进行版本控制
  • Schema变更需同步更新文档
  • 重大变更应创建新版本
  • 旧版本应保持兼容性至少一个大版本周期

本节来源