2. GraphQL 基础回顾:核心概念与类型系统
在深入 BFF 层的请求合并与数据加载优化之前,必须牢固掌握 GraphQL 的核心概念。本章将系统回顾 Schema、Query、Mutation、Subscription 四大支柱,以及类型系统与查询语法,为后续的优化策略打下坚实的理论基础。
2.1 GraphQL 核心概念全景
GraphQL 是一种用于 API 的查询语言,由 Facebook 于 2015 年开源。它让客户端能够精确地声明所需的数据结构,从而解决 RESTful API 中常见的“过度获取”和“获取不足”问题。其核心由以下四个概念构成:
- Schema(模式):API 的契约与类型定义文件,描述了所有可用的数据对象、字段及其关系。
- Query(查询):用于从服务端读取数据,类似于 REST 中的 GET 请求。
- Mutation(变更):用于修改服务端数据(增、删、改),类似于 REST 中的 POST/PUT/DELETE。
- Subscription(订阅):用于建立长连接,实时接收服务端推送的数据更新。
关键认知: 在 BFF 层设计中,Query 是请求合并优化的主战场,而 Schema 的设计质量直接决定了数据加载的效率。理解这三者的关系是优化工作的起点。
2.2 类型系统:GraphQL 的基石
GraphQL 拥有强类型系统,每个字段都必须声明其返回类型。类型系统是 Schema 的核心,也是实现数据加载优化的基础——因为类型决定了数据如何被解析和组合。
2.2.1 标量类型与对象类型
| 类型类别 | 具体类型 | 说明 | BFF 优化关注点 |
|---|---|---|---|
| 标量类型 | Int, Float, String, Boolean, ID |
叶子节点,不可再拆分 | 直接返回,无需进一步解析 |
| 对象类型 | type User { ... } |
包含多个字段的复合类型 | 需要 Resolver 逐字段解析,是 DataLoader 优化的主要目标 |
| 枚举类型 | enum Status { ACTIVE INACTIVE } |
限定字段的取值范围 | 减少无效数据传递 |
| 列表类型 | [User] |
表示一组对象 | 批量加载的典型场景,N+1 问题高发区 |
| 非空类型 | String! |
字段值不能为 null | 减少客户端空值判断逻辑 |
2.2.2 接口与联合类型
在 BFF 层,接口(Interface)和联合类型(Union)常用于处理多态数据,例如“搜索结果”可能包含用户、文章、商品三种类型。
# 接口定义
interface SearchResult {
id: ID!
title: String!
}
type User implements SearchResult {
id: ID!
title: String!
avatar: String
}
type Article implements SearchResult {
id: ID!
title: String!
content: String
}
# 联合类型
union MediaItem = Image | Video
type Query {
search(keyword: String!): [SearchResult!]!
mediaLibrary: [MediaItem!]!
}
优化陷阱: 联合类型和接口在解析时通常需要额外的类型判断逻辑(
__typename),这会增加 Resolver 的复杂度。在 BFF 层,如果多态场景不复杂,建议优先使用对象类型加可选字段,以减少解析开销。
2.3 Query 与 Mutation 的查询语法
GraphQL 的查询语法极其灵活,客户端可以精确控制返回的数据结构。理解查询语法是后续实现“请求合并”的前提——因为合并的本质是将多个查询片段组合成一个高效的查询树。
2.3.1 基础查询与参数传递
# 查询单个用户,带参数
query GetUser($userId: ID!) {
user(id: $userId) {
id
name
email
profile {
age
city
}
}
}
# 查询变量
# 变量:{ "userId": "123" }
2.3.2 别名与片段:请求合并的语法基础
别名(Alias) 允许在同一个查询中请求同一个字段多次,片段(Fragment) 允许复用字段集合。这两者是 BFF 层实现查询合并的核心语法工具。
# 使用别名同时查询两个用户
query GetTwoUsers {
user1: user(id: "1") {
...UserFields
}
user2: user(id: "2") {
...UserFields
}
}
# 定义片段,复用字段
fragment UserFields on User {
id
name
email
profile {
age
city
}
}
2.3.3 Mutation 的典型写法
# 变更操作,通常包含输入类型和返回类型
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
createdAt
author {
id
name
}
}
}
# 输入变量
# { "input": { "title": "GraphQL 优化", "content": "..." } }
2.4 Subscription:实时数据推送
Subscription 使用 WebSocket 等持久连接协议,在 BFF 层常用于实时通知、数据变更推送等场景。其语法与 Query 类似,但使用 subscription 关键字。
subscription OnPostCreated {
postCreated {
id
title
author {
name
}
}
}
BFF 层实践要点: Subscription 在 BFF 层通常作为“代理”存在——BFF 订阅下游微服务的事件,然后推送给前端。这要求 BFF 层具备连接管理和事件路由能力,是请求合并之外的另一大优化维度。