本文最初发布于博客,由 infoq 中文站翻译并分享。
这个昨天在推特上爆发了,我想应该用更长的篇幅回顾一下作者的一些观点,澄清一些误解,我们一个个过一遍。
【推文 1 】graphql 使你的公共 api 等同于一个通用数据库,更糟糕的是——一个通用图形数据库,维护工作量高得惊人;锁定查询功能意味着你只是在运行普通的 api,但不锁定它意味着无限的性能工作。()
主张 1 :graphql 使您的公共 api 等同于通用图形数据库
根据作者的说法,graphql 使我们的公共 api 等同于通用的图形数据库。如果您在过去几年里经常听说过 graphql,那么可能知道这是 graphql 最常见的误解之一。尽管有些 api ,但就像大多数 api 的风格一样,graphql api 是通用的还是特定的,是您自己决定的。
一个通用的 graphql api 与大多数人认为的最佳实践背道而驰。事实上,graphql 规范一直拒绝通用/类数据库功能(如过滤、排序等)的提议。
避免通用/类数据库模式在上列为最佳实践,没有比这更明确的了。
构建一个 graphql 最好的模式是告诉客户端如何使用数据,而不是镜像旧的数据库模式。
公共 api 确实比内部 api 更通用一些,需要预测各种各样的场景调用,有时在客户端调用的公共 api 中也需要更“原始”的形式展示数据。虽然这不是 graphql。
主张 2 :维护工作量高得离谱
这是个很有意思的轶闻。相比其它风格的 api,我没有发现使用 graphql 更难维护,实际上可以说它更容易维护,但这可能是有点自欺,因为很多 graphql 项目都是新领域,通常比累积了技术债务的遗留 api 具有更好的可维护性。准确地说,我认为维护更多的是与软件本身的编写相关,而不是具体的技术选择。我并没有在这些推文中看到一个强有力的例子来说明 graphql 为什么难以维护。
主张 3 :锁定查询功能意味着你只是在使用普通的 api,而不锁定则意味着无限的性能工作
作者谈及的“锁定”像是持久查询,这意味着让 graphql “打开”(客户端能执行任意查询)会导致无限的性能工作,以下推文中的观点可能说得更具体。如我们所知,一个设计和实现良好的 graphql api 能够很好地处理任意查询,并在合理时间内提供查询服务。
锁定查询功能并不意味着你只是在使用“普通” api(不管这意味着什么),像持久化查询的服务仍然提供了很大的灵活性,服务端团队并不需要为每个新的持久化查询做额外的工作,模式(schema)公开了这些可能性,即使查询以这种方式列入白名单(通常在构建时),但实际的调用仍由客户端决定。
【推文 2 】在任何应用程序中,管理对象生命周期都很困难,因为它们在内部具有图形化性质,公开时情况会更糟。如果可以充分考虑公开哪些连接,几乎每个系统都更容易预测和维护。 ()
我绝对同意,但这似乎与 graphql 无关。graphql api 公开的内容就是您选择公开的内容,而无需公开内部细节;重点是 graphql 中的连接是人为设计的,另外在 graphql 中避免不可预知的对象访问,与在典型的基于资源的 api 中所做相似。类似的想法也在下面的推文中。
【推文 3 】非图 api 的不良性能和生命周期错误已经够让我们心惊胆战了,面对如此大量的多对多连接时,还需要用户和我们自己来预测?这可不是人类能干的活儿,问题都成了吓人的玩意儿。()
我认为这是 graphql 的一种权衡,graphql api 往往不能像其它 api 那样进行优化,因为它提供了一定的灵活性,这是设计的一部分,但对有些人来说这可能是设计的缺陷(尽管开销通常很小)。在构建 graphql api 时,有很多方法可以进行改善,比如正确设置批处理和缓存数据加载;如果您将对象类型视为 “资源”或“端点”时,安全性与其它 api 都非常相似。
【推文 4 】在 sql 数据库中,典型的 graphql 需要查询中的嵌套查询和无限连接,这些都是众所周知的可靠性、性能、代码扩展性和理解性问题,是所有通用图形 api 问题的一种体现。()
graphql 不需要那么复杂的 sql 查询。其实,我很好奇作者是怎么得到的结论,这通常不是 graphql 执行导致的查询。如果说 graphql 有什么不成熟的实现,就是会为每个解析器产生大量的小查询,通过数据加载器,可以看到像是一组查询的子集(其中很多是 select…where…in 查询)。如果需要构建预解析器或使用 graphql-to-sql 的自动生成,才会用到查询中的嵌套查询和无限连接,只是在实际中不常见。
graphql 并不是一个通用的 graph api。
【最后一条推文】另外还有一件事要讨论,如何让系统以可预测的、有限度的方式变慢,往往比让系统以不可预测的、无限度的方式变慢更有用,“不可预测”和“无限”延时通常同时出现。()
我非常同意最后一条推文,这是一个很好的观点。graphql api,尤其是公共 api,不可能像服务器驱动的用例那样具有可预测性,因为服务器驱动的服务调用是预知并单独优化过的,简单实现的 graphql api 肯定会导致非常低效的数据加载。还好 graphql 的可观察性工具、数据加载技术和类库现在都有了,让我的 graphql api 具有预测性而且速度很快。
如果您不需要 graphql 的特性,那么的确不如直接丢几个 rpc 端点那样简单,而且用起来免不了带来更多的认知负担。
这个主题可以作为提醒在构建 graphql api 时不要做什么,然而有些地方像稻草人一样,错误地描述了 graphql 构建的目的。文末给大家一些小贴士:
不要将 graphql api 设计成一个图形数据库,也不要根据数据库模式设计您的 api。
尽可能使用异步/批处理数据加载(如),不要创建基于预查询或 gql-to-sql 工具的复杂 sql 查询(常规经验)。
如果不能有效地支持通用特性,应避免使用它们,在需要且性能满足时再添加。
如果 graphql 设计用来解决的那些问题您遇不到,那么也没有必要用它。
原文链接:
评论