是什麼讓我放棄了 restful api?瞭解清楚後我全面擁抱 GraphQL

背景

REST 作爲一種現代網絡應用非常流行的軟件架構風格,自從 Roy Fielding 博士在 2000 年他的博士論文中提出來到現在已經有了 20 年的歷史。它的簡單易用性,可擴展性,伸縮性受到廣大 Web 開發者的喜愛。

REST 的 API 配合 JSON 格式的數據交換,使得前後端分離、數據交互變得非常容易,而且也已經成爲了目前 Web 領域最受歡迎的軟件架構設計模式。

但隨着 REST API 的流行和發展,它的缺點也暴露了出來:

那我們來分析一下,當前端需求變化,涉及到改動舊需求時,會有以下這些情況:

做加法

產品需求增加,頁面需要增加功能,數據也就相應的要增加顯示,那麼 REST 接口也需要做增加,這種無可厚非。

做減法

產品需求減少,頁面需要減少功能,或者減少某些信息顯示,那麼數據就要做減法。

一種通常懶惰的做法是,前端不與後端溝通,僅在前端對數據選擇性顯示。

因爲後端接口能夠滿足數據需要,僅僅是在做顯示的時候對數據進行了選擇性顯示,但接口的數據是存在冗餘的,這種情況一個是存在數據泄露風險,另外就是數據量過大時造成網絡流量過大,頁面加載緩慢,用戶流量費白白消耗,用戶體驗就會下降。

另外一種做法就是告知後端,要麼開發新的接口,要麼,修改舊接口,刪掉冗餘字段。

但一般來說,開發新接口往往是後端開發人員會選擇的方案,因爲這個方案對現有系統的影響最低,不會有額外的風險。

修改舊接口刪除冗餘數據的方案往往開發人員不會選擇,這是爲什麼呢?

這就涉及到了系統的穩定性問題了,舊接口往往不止是一個地方在用,很有可能很多頁面、設置不同客戶端、不同服務都調用了這個接口獲取數據,不做詳細的調查,是不可能知道到底舊接口被調用了多少次,一旦改動舊接口,涉及範圍可能非常大,往往會引起其他地方出現崩潰。改動舊接口成本太高,所以往往不會被採取。

同時做加減法:

既有加法,又有減法,其實這種就跟新需求沒啥區別,前端需要重做頁面,後端需要新寫接口滿足前端需要,但是舊接口還是不能輕舉妄動(除非確定只有這一處調用纔可以刪除)。

往往這個時候,其實用到的數據大多都是來自於同一個 DO 或者 DTO,不過是在 REST 接口組裝數據時,用不同的 VO 來封裝不同字段,或者,使用同樣的 VO,組裝數據時做刪減。

看到這些問題是不是覺得令人頭大?

所以需求頻繁改動是萬惡之源,當產品小哥哥改動需求時,程序員小哥哥可能正提着鐵鍬趕來......

那麼有沒有一種方案或者框架,可以使得在用到同一個領域模型(DO 或者 DTO)的數據時,前端對於這個模型的數據字段需求的改動,後端可以根據前端的改動和需要,自動適配,自動組裝需要的字段,返回給前端呢?如果能這樣做的話,那麼後端程序猿小哥可能要開心死了,前端妹子也不用那麼苦口婆心地勸說後端小哥哥了。

所以 GraphQL 隆重出世了!那麼問題來了!

Part 1 What is GraphQL

GraphQL 簡介

簡單的來說,它

它的工作模式是這樣子的:

GraphQL 對比 REST API 有什麼好處?

REST API 的接口靈活性差、接口操作流程繁瑣,GraphQL 的聲明式數據獲取,使得接口數據精確返回,數據查詢流程簡潔,照顧了客戶端的靈活性。

客戶端拓展功能時要不斷編寫新接口(依賴於服務端),GraphQL 中一個服務僅暴露一個 GraphQL 層,消除了服務器對數據格式的硬性規定,客戶端按需請求數據,可進行單獨維護和改進。

REST API 基於 HTTP 協議,不能靈活選擇網絡協議,而傳輸層無關、數據庫技術無關使得 GraphQL 有更加靈活的技術棧選擇,能夠實現在網絡協議層面優化應用。

舉個經典的例子:前端向後端請求一個 book 對象的數據及其作者信息。

我用動圖來分別演示下 REST 和 GraphQL 是怎麼樣的一個過程。

先看 REST API 的做法:

REST API 獲取數據

再來看 GraphQL 是怎麼做的:

GraphQL 獲取數據

可以看出其中的區別:

Endpoints 對比

REST API's Endpoints

圖數據庫模式的數據查詢

那我們已經能看到 GraphQL 的先進性,接下來看看它是怎麼做的。

GraphQL 思考模式

使用 GraphQL 接口設計獲取數據需要三步:

GraphQL 獲取數據三步驟

  1. 首先要設計數據模型,用來描述數據對象,它的作用可以看做是 VO,用於告知 GraphQL 如何來描述定義的數據,爲下一步查詢返回做準備;
  2. 前端使用模式查詢語言(Schema)來描述需要請求的數據對象類型和具體需要的字段(稱之爲聲明式數據獲取);
  3. 後端 GraphQL 通過前端傳過來的請求,根據需要,自動組裝數據字段,返回給前端。

GraphQL 的這種思考模式是不是完美解決了之前遇到的問題呢?!

總結它的好處:

在它的設計思想中,GraphQL 以圖的形式將整個 Web 服務中的資源展示出來,客戶端可以按照其需求自行調用,類似添加字段的需求其實就不再需要後端多次修改了。

創建 GraphQL 服務器的最終目標是:

允許查詢通過圖和節點的形式去獲取數據。

GraphQL 執行邏輯

有人會問:

答案是:NO!不需要!

它完全可以以一種不侵入的方式來部署,將它作爲前後端的中間服務,也就是,現在開始逐漸流行的 前端 —— 中端 —— 後端 的三層結構模式來部署!

那就來看一下這樣的部署模式圖:

GraphQL 執行邏輯

也就是說,完全可以搭建一個 GraphQL 服務器,專門來處理前端請求,並處理後端服務獲取的數據,重新進行組裝、篩選、過濾,將完美符合前端需要的數據返回。

新的開發需求可以直接就使用 GraphQL 服務來獲取數據了,以前已經上線的功能無需改動,還是使用原有請求調用 REST 接口的方式,最低程度的降低更換 GraphQL 帶來的技術成本問題!

如果沒有那麼多成本來支撐改造,那麼就不需要改造!

只有當原有需求發生變化,需要對原功能進行修改時,就可以換成 GraphQL 了。

GraphQL 應用的基本架構

下圖是一個 GraphQL 應用的基本架構,其中客戶端只和 GraphQL 層進行 API 交互,而 GraphQL 層再往後接入各種數據源。這樣一來,只要是數據源有的數據, GraphQL 層都可以讓客戶端按需獲取,不必專門再去定接口了。

GraphQL 應用基本架構

一個 GraphQL 服務僅暴露一個 GraphQL Endpoint,可以按照業務來進行區分,部署多個 GraphQL 服務,分管不同的業務數據,這樣就可以避免單服務器壓力過大的問題了。

GraphQL 特點總結

Part 2 Schema & Type

GraphQL 支持的數據操作

GraphQL 對數據支持的操作有:

GraphQL 支持的操作

GraphQL 的核心概念:圖表模式(Schema)

要想要設計 GraphQL 的數據模型,用來描述你的業務數據,那麼就必須要有一套 Schema 語法來做支撐。

想要描述數據,就必須離不開數據類型的定義。所以 GraphQL 設計了一套 Schema 模式(可以理解爲語法),其中最重要的就是數據類型的定義和支持。

那麼類型(Type)就是模式(Schema)最核心的東西了。

什麼是類型?

那麼就分別來介紹下兩種類型。

標量類型(Scalar Type)

標量是 GraphQL 類型系統中最小的顆粒。類似於 Java、C# 中的基本類型。

其中內建標量主要有:

Scalar Type

上面的類型僅僅是 GraphQL 默認內置的類型,當然,爲了保證最大的靈活性,GraphQL 還可以很靈活的自行創建標量類型。

對象類型(Object Type)

僅有標量類型是不能滿足複雜抽象數據模型的需要,這時候我們可以使用對象類型。

通過對象模型來構建 GraphQL 中關於一個數據模型的形狀,同時還可以聲明各個模型之間的內在關聯(一對多、一對一或多對多)。

對象類型的定義可以參考下圖:

對象模型引入關聯關係

是不是很方便呢?我們可以像設計類圖一樣來設計 GraphQL 的對象模型。

類型修飾符(Type Modifier)

那麼,類型系統僅僅只有類型定義是不夠的,我們還需要對類型進行更廣泛性的描述。

類型修飾符就是用來修飾類型,以達到額外的數據類型要求控制。

比如:

在描述數據模型(模式 Schema)時,就可以對字段施加限制條件。

例如定義了一個名爲 User 的對象類型,並對其字段進行定義和施加限制條件:

User 字段控制

那麼,返回數據時,像下面這種情況就是不允許的:

錯誤的表示

Graphql 會根據 Schema Type 來自動返回正確的數據:

正確的表示

其他類型

除了上面的,Graphql 還有一些其他類型來更好的引入面向對象的設計思想:

比如定義了一個接口類型:

那麼就可以實現該接口:

比如定義了一個輸入類型:

前端發送變更請求時就可以使用(通過參數來指定輸入的類型):

所以,這樣面向對象的設計方式,真的對後端開發人員特別友好!而且前端 MVVM 框架流行以來,面向對象的設計思想也越來越流行,前端使用 Graphql 也會得心應手。

Part 3 GraphQL 技術接入架構

Graphql 技術接入架構

那麼,該怎麼設計來接入我們現有的系統中呢?

直連數據庫的接入

集成現有服務的 GraphQL 層

混合接入方式

可以說是非常靈活了!你都不用擔心會給你帶來任何的麻煩。

服務端實現

在服務端, GraphQL 服務器可用任何可構建 Web 服務器的語言實現。有以下語言的實現供參考:

C# / .NET

Clojure

Elixir

Erlang

Go

Groovy

Java

JavaScript

Julia

Kotlin

Perl

PHP

Python

R

Ruby

Rust

Scala

Swift

種類繁多,幾乎流行的語言都有支持。

客戶端實現

在客戶端,Graphql Client 目前有下面的語言支持:

C# / .NET

Clojurescript

Elm

Flutter

Go

Java / Android

JavaScript

Julia

Swift / Objective-C iOS

Python

R

覆蓋了衆多客戶端設計語言,而其他語言的支持也在推進中。

Graphql 的一些服務

整理了下目前比較流行的服務框架:

Graphql 的一些工具

想要獲取更多關於 Graphql 的一些框架、工具,可以去 awesome-graphql:一個神奇的社區,維護一系列庫、資源等,地址是

https://github.com/chentsulin/awesome-graphql。

想要學習更多 Graphql 的知識,可以去 GraphQL.cn。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://www.toutiao.com/i6833818331884028419