2
2
3
3
构建面向视图的数据时, 不可避免会出现数据拼接的场景. 根据视图数据的复杂程度, 拼接的难度会有很大的差异.
4
4
5
- 三层的数据拼接会比两层的复杂, 四层的肯定会更加麻烦.
6
-
7
5
``` json
8
6
[
9
7
{
@@ -39,11 +37,11 @@ for t in teams:
39
37
...
40
38
```
41
39
42
- 过程式的数据处理对调整和阅读都不友好, 循环和拼接容易产生不通用且不易维护的代码. ORM 则存在使用场景的约束 .
40
+ 过程式的数据处理对调整和阅读都不友好, 循环和拼接容易产生不通用且不易维护的代码. 添加和修改也很麻烦 .
43
41
44
- GraphQL 带来的通过声明描述数据结构是一个好的方向, Graqph Query Language, 作为一种声明式的查询语言, 扮演的是一系列特定资源的获取和组合 . 从定位上来说, GraphQL 用来给项目的前端提供数据是一种错误的用法 , 它的定位和 SQL 是类似的, 为服务端获取数据提供便利. 并且可以尽量不用考虑权限, 限速等问题.
42
+ GraphQL 带来的通过声明描述数据结构是一个好的方向, Graph Query Language, 作为一种声明式的查询语言, 服务于一系列特定资源的获取和组合 . 从定位上来说, GraphQL 用来给项目的前端提供数据其实是一种错误的用法 , 它的定位和 SQL 是类似的, 为服务端获取数据提供便利. 并且可以尽量不用考虑权限, 限速等问题.
45
43
46
- 一段复杂的GraphQL query 和一段复杂的 ORM SQL 的定位是类似的. 在获取到了需要的固化数据之后 , 再通过任意一种协议, 比如 http, 比如 rpc 等方式传递给前端使用. 前端作为展示层, 被动地使用后端接口数据即可. 现在很多前端直接使用 GraphQL 来组合查询, 从职位划分来说做的是一部分后端的工作 . 把GraphQL 放在 client 和 server 之间并不是一个理想的定位. 就像把 SQL 查询暴露给 client 一样.
44
+ 一段复杂的GraphQL query 和一段复杂的 ORM SQL 的功能很相似, 只是GraphQL更擅长组合数据. 在获取到了需要的视图数据之后 , 再通过任意一种协议, 比如 http, 比如 rpc 等方式传递给前端使用. 前端作为展示层, 被动地使用后端接口数据即可. 现在很多前端直接使用 GraphQL 来组合查询, 从职位划分来说等于插手了一部分后端的工作 . 把GraphQL 放在 client 和 server 之间并不是一个理想的定位. 就像把 SQL 查询暴露给 client 一样. (对数据的管理分散在多个环节不利于项目维护.)
47
45
48
46
GraphQL 的思路是通过 Query 确定最终的查询结构, 一层层驱动后端的 resolver 来构造数据.
49
47
@@ -52,15 +50,18 @@ GraphQL 的思路是通过 Query 确定最终的查询结构, 一层层驱动后
52
50
- 无法描述尺寸不确定的递归结构
53
51
- key 不确定的 Dict 结构
54
52
- Query 比较复杂的话, 性能问题不容易优化.
55
- - 无法对查询到的数据做比较精细的后期处理 (middleware 的入口是query 数据的顶层)
53
+ - 数据的后处理不方便
54
+ - 要使用它定义的一套类型
56
55
57
56
等等.
58
57
59
- 总结来说, GraphQL 在提供了视图数据方面, 有查询灵活度高的优点, 同时存在获取的数据后期调整比较麻烦, 以及架构侵入较大等缺点. 比如 GraphQL 获取到多层数据后要做层级聚合统计, 就需要重新便利一遍树状数据来处理. 框架本身没有设计合适的下层数据处理完之后触发回调的钩子.
58
+ 总体来说, GraphQL 在提供视图数据方面, 有查询灵活度高的优点, 但存在获取的数据后期调整比较麻烦, 以及架构侵入较大等缺点. 比如 GraphQL 获取到多层数据后要做层级聚合统计, 就需要重新便利一遍树状数据来处理. 框架本身没有设计合适的下层数据处理完之后触发回调的钩子. (这恰恰是对视图调整很有用的)
59
+
60
+ 思考后会发现, 其实我并不需要GraphQL 那个灵活的查询组合功能. 在处理视图数据的时候, GraphQL 最大的启发是他申明式的数据描述方式.
60
61
61
- 稍加思考会发现, 我并不需要GraphQL 那个灵活的查询组合功能. 在处理视图数据的时候, GraphQL 最大的启发是他申明式的数据描述方式 .
62
+ 以 graphene-python 为例, Query对象可以支持灵活的 GraphQL 查询, 比如挑选字段, 或者重命名等等 .
62
63
63
- 以 graphene-python 为例:
64
+ 并且随着功能的添加, Query 里面会增加越来越多的内容.
64
65
65
66
``` python
66
67
from graphene import ObjectType, String, Schema
@@ -75,17 +76,13 @@ class Query(ObjectType):
75
76
def resolve_goodbye (root , info ):
76
77
return ' See ya!'
77
78
schema = Schema(query = Query)
78
- ```
79
79
80
- 这样的Query对象可以支持灵活的 GraphQL 查询, 比如挑选字段, 或者重命名等等.
81
-
82
- ``` python
83
80
query_with_argument = ' { hello(firstName: "GraphQL") }'
84
81
result = schema.execute(query_with_argument)
85
82
print (result.data[' hello' ])
86
83
```
87
84
88
- 那如果我并不需要去查询 , 而是直接把 Query对象直接就作为标准视图结构呢 ?
85
+ 那如果我并不需要去编写查询 , 而是直接把 Query 直接当成一个描述语句来使用呢? 那我不就省下了额外描述的成本了 ?
89
86
90
87
``` python
91
88
from pydantic import BaseModel
@@ -107,7 +104,7 @@ async def main():
107
104
108
105
面向 schema 进行定制化的描述. 借助 pydantic 强大的类型转换和检查的功能, 来实现申明式的数据结构描述.
109
106
110
- 优点就是把大而全的单一查询入口, 替换成了一个个小巧灵活的定制化 schema 描述.
107
+ ** 优点就是把大而全的单一查询入口, 替换成了一个个小巧灵活的定制化 schema 描述.**
111
108
112
109
> 基于 schema 的声明式描述让整个结构摆脱了 GraphQL 的架构约束, 只用常用的 type annotation 就把定义给实现了.
113
110
>
@@ -116,19 +113,22 @@ async def main():
116
113
117
114
## 什么是面向组合的模式?
118
115
119
- 面向组合的开发模式就是在这个简单case 的基础上, 扩展的一套开发模式. 核心概念就是在 ` 根数据 ` 的基础上, 通过一层层定义需要的扩展字段, 然后交给 ` Resolver ` 来填充所有的数据.
116
+ 面向组合的开发模式就是在这个简单例子的基础上, 逐步扩展出来的一套开发模式.
120
117
118
+ 核心概念就是在` 根数据 ` 的基础上, 描述每一层需要的扩展的字段和查询方式, 然后交给 ` Resolver ` 来填充所有的数据.
121
119
122
120
罗列一下, 这套开发模式有以下这些能力:
123
121
124
122
- 可以方便的描述组合体数据的 schema, 然后resolve出完整数据, 定义方式简单 (借助 pydantic or dataclass)
125
- - 任意深度 , 任意类型.
126
- - 读取全局参数, 读取祖先节点数据
127
- - 每层 ` resolve ` 完子孙数据后, 有 post 方法来操作数据
123
+ - 任意层级 , 任意类型.
124
+ - 可以读取全局参数, 可以跨层级向下传递数据
125
+ - 每一层都有后处理数据的能力
128
126
- 可以挑选所需的字段
129
127
- 解决 N+1 查询相关的性能问题
130
- - 各个 service 仅需提供通用的 loader, 用于数据拼装
131
- - 保证前后端对修改的感知能力, 易于修改重构 (借助OpenAPI)
128
+ - 架构简单, 各个 service 仅需提供通用的 loader, 用于数据拼装
129
+ - 借助OpenAPI, 前端对后端操作简化为sdk 方法调用.
130
+ - 调整视图数据很容易, 让前端真正做到开箱即用.
131
+ - 对优化友好, 只要保证输出一致, 内部重构对API使用者无感.
132
132
133
133
134
134
> 可以很容易联想到, 我们获得了一个 API 提供一个page 所需数据的能力, 这会让前后端接口关系变得更简单.
@@ -198,8 +198,6 @@ Mini jira 包含了常见的敏捷开发中的各种概念和其之间的关系.
198
198
199
199
只需要描述好 Task 要扩展的字段, Story 要扩展的字段, 然后 Resolver 就会帮你处理完后续的所有事情.
200
200
201
- 修改调整都变得非常简单轻松.
202
-
203
201
``` python
204
202
from typing import Optional
205
203
from pydantic2_resolve import LoaderDepend as LD
0 commit comments