尽管名为GraphQL,但它并不是一种简单的查询语言。这是一个全面解决现代应用与云服务之间连接问题的方案。因此,它构成了现代应用程序开发堆栈中一个新的重要层的基础:数据图。这个新层将公司的所有应用程序数据和服务集中在一个地方,具有一致的、安全的、易用的界面,这样任何人都可以在最小的摩擦下使用它。
在Apollo,我们从2015年就开始构建行业领先的数据图技术,我们估计我们的软件现在在超过90%的GraphQL实现中使用。多年来,我们与各种规模的公司中实现GraphQL的开发人员进行了数千次对话。我们希望分享我们所学到的知识,因此我们将他们的经验提炼为一组创建、维护和操作数据图的最佳实践。我们在这里将它们以10个GraphQL原则的形式呈现,分为三类,其格式受到了Twelve Factor应用程序的启发。
完整性原则
确保图定义良好、稳定且一致
1. 一个图
您的公司应该有一个统一的图表,而不是由每个团队创建的多个图表。
通过一个图形,你可以最大化GraphQL的价值:
- 可以通过一个查询访问更多的数据和服务
- 代码、查询、技能和经验可以跨团队移植
- 所有图形用户都可以查看的所有可用数据的中心目录
- 实现成本最小化,因为图形实现工作不重复
- 图的中央管理——例如,统一的访问控制策略——成为可能
当团队在没有协调工作的情况下创建他们自己的图表时,他们的图表几乎不可避免地会开始重叠,以不兼容的方式向图表添加相同的数据。在最好的情况下,这是昂贵的返工;在最坏的情况下,它会造成混乱。在公司的数据图采用过程中,应该尽可能早地遵循这一原则。
2. 联合实施
尽管只有一个图,但该图的实现应该跨多个团队联合。
如果没有高度专门化的基础设施,单片架构很难扩展,数据图也不例外。与在单个代码库中实现组织的整个数据图层不同,定义和实现图的责任应该划分到多个团队中。每个团队都应该负责维护公开他们的数据和服务的模式部分,同时拥有独立开发和在他们自己的发布周期上操作的灵活性。
这维护了图的单一、统一视图的价值,同时保持了整个公司的开发工作的分离。
3.跟踪注册表中的模式
应该有一个单一的事实来源来记录和跟踪图表。
就像在版本控制系统中跟踪源代码很重要一样,在模式注册表中跟踪图形的定义也很重要。您的公司应该有一个模式注册表,它是图形的权威定义,而不是依赖于当前正在运行的进程或在开发人员的笔记本上检入的任何代码。像一个源代码控制系统,注册表的模式存储修改图,谁让他们的历史,它应该理解图像的多个版本的概念(例如,登台和生产,或不同的开发分支)的方式相似的软件开发过程。
模式注册中心应该成为系统的中心,为开发人员工具、工作流或任何业务流程提供支持,这些业务流程将受益于对数据图的感知,以及对数据图的任何实际或建议的更改。
敏捷原则
快速展开图形并不断调整它以适应不断变化的需求
4. 抽象的,需求模式法
模式应该作为一个抽象层,为使用者提供灵活性,同时隐藏服务实现细节。
GraphQL的很大一部分价值在于提供了服务和使用者之间的抽象,因此该模式不应该与特定的服务实现或特定的使用者紧密耦合,因为它们现在已经存在了。通过将实现细节排除在模式之外,应该可以重构实现图的服务——例如,从一个整体转换到微服务,或者改变服务实现的语言——而不影响该领域的应用程序。同样,模式不应该与特定应用程序获取数据的方式紧密耦合。如果新应用程序的功能与现有的应用程序类似,那么应该可以编写新应用程序,而对图形的修改应该最小。
要实现这一点,可以使用面向需求的模式的标准:该模式侧重于为应用程序开发人员提供良好的开发体验,并根据现有图构建新功能。以这个标准为目标将有助于防止图与将来可能更改的服务实现耦合,并有助于增加添加到图中的每个字段的重用价值。
5. 使用敏捷方法进行模式开发
模式应该根据实际需求逐步构建,并随着时间的推移平稳地演进。
提前定义组织所有数据的“完美模式”可能很有诱惑力。相反,真正使模式有价值的是它遵循实际用户需求的程度,而这些需求从来都不是预先完全知道的,并且是不断变化的。实现“完美模式”的真正途径是使图易于根据实际需要进行演化。
不应该推测性地将字段添加到模式中。理想情况下,每个字段应该只在响应消费者对附加功能的具体需求时添加,而设计的目的是最大限度地让其他有类似需求的消费者重用。
更新图形应该是一个连续的过程。与其每隔6个月或12个月发布一个新的图形“版本”,不如在必要时每天多次更改图形。可以随时添加新字段。要删除字段,首先要弃用它,然后在没有使用者使用它时删除它。模式注册表支持图形的这种敏捷演化,以及使每个人都知道可能影响他们的更改的流程和工具。这确保了只有完全审查过的变更才能投入生产。
6. 迭代地提高性能
性能管理应该是一个连续的、数据驱动的过程,能够平稳地适应不断变化的查询负载和服务实现。
数据图层是讨论服务团队和使用其服务的应用程序开发人员之间的性能和容量的最佳位置。这个对话应该是一个持续的过程,它使服务开发人员能够持续地、主动地了解消费者打算用他们的服务做什么。
与其优化图形的每一种可能的用法,不如将重点放在支持生产中所需的实际查询形状上。工具应该提取提议的新查询形状,并在它们投入生产之前将它们呈现给所有受影响的服务团队,这些团队具有延迟需求和预计的查询量。一旦查询进入生产环境,就应该持续监视它的性能。如果遵循这一原则,那么很容易将问题追溯到行为不符合预期的服务。
7. 使用图形元数据来增强开发人员的能力
开发人员应该在整个开发过程中对图形有丰富的认识。
GraphQL的主要价值在于它为开发人员提供了巨大的生产力提升。为了最大限度地提高性能,开发人员的工具应该让他们对数据图有普遍的认识,并贯穿于他们在整个开发生命周期中使用的所有工具。
每当开发人员进行与管理数据或连接到服务相关的工作时,他们的工具应该将关于图的实时信息放在手边。这些信息应该是最新的,工具应该是高度智能的,以有用和强大的方式将图形感知应用到当前的情况。如果处理得当,不仅会提高开发人员的工作效率和幸福感,而且GraphQL还会成为连接前端和后端团队的纽带,在整个开发生命周期中实现无缝对话。
一些数据图形感知工具的功能的实际例子包括:
- 开发人员可以在他们的编辑器中享受所有可用图形数据和服务的实时文档,而且总是最新的。
- 有关废弃字段的信息可以传播到使用这些字段的开发人员的编辑器中,并提供建议的替代方案
- 根据实时生产数据,在开发人员输入查询时,可以向他们显示查询的估计成本(以延迟或服务器资源计算)。
- 运维团队可以跟踪后端服务的负载,将其追溯到特定的应用程序、版本、功能,甚至代码行,从而全面了解开发人员如何使用其服务。
- 当服务开发人员对其模式进行更改时,可以作为持续集成过程的一部分自动确定更改的影响。如果更改会破坏现有的客户机(根据重播最近的生产使用情况确定),那么服务开发人员可以确定将受到影响的精确客户机、版本和开发人员。
- 当应用程序开发人员构建特性时,可以从他们的代码中提取支持这些特性的新查询,并与操作团队共享。有了这种认识,操作团队就可以提前提供所需的能力,并在开发过程的早期介入,如果查询不能在预期的范围内得到批准的话。
- 当应用程序是用类型化语言(如TypeScript、Java或Swift)开发的时候,类型信息可以从服务类型声明一直传播到应用程序的每一行代码,从而确保全堆栈类型的正确性和对错误的即时反馈。
操作原则
安全地将图部署到大规模生产环境中
8. 存取和需求控制
在每个客户端基础上授予对图形的访问权,并管理客户端可以访问什么以及如何访问它。
数据图中的授权具有两个同样重要的方面:访问控制和需求控制,前者表示允许用户访问哪些对象和字段,后者表示允许用户如何(以及多少)访问这些资源。虽然经常讨论访问控制,但也需要注意需求控制,因为它在GraphQL的任何生产部署中都是至关重要的。允许用户不考虑成本执行任何可能的查询是错误的,因为用户没有能力管理它对生产系统的影响。访问和需求控制都必须在充分了解数据图的语义和性能的情况下执行。在不分析实际发送的查询的情况下,仅将用户限制在每分钟特定的查询数量是不够的,因为查询可以访问大量的服务,而且查询的成本可以在多个数量级上变化。
验证在数据图也有两个方面:请求操作的应用程序,并使用这个应用程序的人。虽然访问控制中心的人使用应用程序,适当的需求控制至少取决于每个应用控制人均控制,应用程序的开发人员,而不是应用程序的用户负责应用程序所使用的特定查询的形状做它的工作。
控制需求的最佳方法包括:
- 当不受信任的用户访问生产系统时,他们应该只发送由经过验证的应用程序开发人员预先注册的查询,而不是允许他们使用应用程序的凭据发送任意查询。对于只分发给可信用户的内部应用程序来说,这有时并不严格。
- 对于预计将发送大量查询的应用程序,团队应该设计一个与更广泛的软件开发周期相一致的查询审批工作流,以便在查询进入生产之前对其进行审查。这可以确保它们不会获取不必要的数据,并且服务器的容量可以支持它们。
- 作为第二道防线,在执行查询之前估计它的成本,并为每个用户和每个应用程序制定查询成本预算,可以防止过度使用预先注册的操作,或者在无法进行预先注册的操作的情况下。
- 开发人员应该能够禁用特定应用程序在生产环境中发送特定查询的能力,无论是作为紧急情况下的安全网,还是发现第三方应用程序以不可接受的方式使用数据图。
9. 结构化的日志
捕获所有图形操作的结构化日志,并利用它们作为了解图形用法的主要工具。
可以捕获关于在图上执行的每个操作(读或写)的大量信息:哪些用户和应用程序执行了该操作、访问了哪些字段、实际执行操作的方式、如何执行操作,等等。这些资料非常宝贵,应当有系统地加以收集,供以后使用。它应该以结构化的、机器可读的格式捕获,而不是文本日志,这样就可以为尽可能多的目的利用它。
图形操作的记录称为跟踪。跟踪应该召集所有相关信息关于一个操作在一个地方,包括业务信息(谁做了手术,访问或改变,哪些功能的应用程序由哪些开发人员,是否成功,如何执行)和纯技术信息(后端服务联系,每个服务如何导致延迟,是否使用缓存)。
因为跟踪真正地捕获了一个图是如何被使用的,它们可以被用于广泛的用途:
- 了解是否可以删除废弃字段,或者是否可以删除仍在访问它的特定客户端,以及它们的重要性
- 根据实时的生产数据,实时地预测一个查询需要多长时间执行,因为开发人员正在他们的IDE中输入查询
- 自动检测生产中的问题(如增加的延迟或错误率)并诊断其根本原因
- 提供权威的审计跟踪,显示哪些用户访问了特定的记录
- 为商业智能查询提供支持(当天气炎热时,人们会在自己所在的地方更频繁地搜索冰激凌吗?)
- 根据API使用情况为合作伙伴生成发票,可以根据访问的特定字段或消耗的资源创建详细的成本模型
所有图形操作的跟踪应该集中在一个中心位置,这样就有了一个权威的跟踪流。然后,这个流可以通过管道进入其他可观察性系统(可能在对不支持graphql的现有系统进行简单转换之后),或者存储在一个或多个数据仓库中供以后使用(根据预算、用例和规模的需要进行汇总和取样)。
10. 将GraphQL层与服务层分离
采用分层架构,将数据图功能分解为单独的层,而不是整合到每个服务中。
在大多数API技术中,客户机不直接与服务器通信,除非在开发中。取而代之的是一种分层的方法,其中将一些问题(如负载平衡、缓存、服务位置或API密钥管理)分解为单独的一层。然后,可以将此层与后端服务分开设计、操作和伸缩。
GraphQL也不例外。与其将一个完整的数据图系统所需的所有功能都放到每个服务中,不如将大多数数据图功能分解成位于客户端和服务之间的独立层,让每个服务专注于服务于实际的客户端请求。这一层可以由多个进程组成,执行访问和需求控制、联合、跟踪收集和潜在的缓存等功能。这一层的某些部分是特定于graphql的,需要深入了解数据图,而负载平衡和客户端身份验证等其他功能可能由现有系统来执行。
即使在只有一个应用程序和一个服务的简单场景中,这个单独的层也是有价值的;否则,属于中间层的功能必须在服务器中实现。在复杂的应用程序中,这一层可能开始看起来像一个地理上分布的系统:通过多个入口点接收传入的查询,处理其中一些在网络的边缘边缘缓存的好处,路由子组件查询多个数据中心的公共云,民营,或由合作伙伴,最后这些组件组装成一个查询结果,同时记录跟踪,这是纪念整个操作。
在某些情况下,此数据图层将使用GraphQL与后端服务通信。但是,最常见的情况是,后端服务保持不变,并继续被它们现有的api(如REST、SOAP、gRPC、Thrift甚至SQL)访问,这些api与数据图对象之间的映射由构成数据图层一部分的服务器完成。
原文:https://principledgraphql.com
讨论:请加入知识星球【首席架构师圈】
最新内容
- 2 days 5 hours ago
- 2 days 7 hours ago
- 2 days 7 hours ago
- 4 days 23 hours ago
- 5 days 6 hours ago
- 5 days 7 hours ago
- 5 days 7 hours ago
- 5 days 7 hours ago
- 1 week 2 days ago
- 1 week 2 days ago