WEBAPI設計原則
WebAPI設計原則
API擁有『永久性』的特性,好壞的設計都會伴隨著,不管未來改版與否,舊版本依然很難拿掉,請謹慎設計。
API設計優先
千萬記得跟技術/非技術的人參與討論,懂技術的規劃一定不平衡不健全
- 探索:確認API的需求以及確認是否現有的API已經能滿足用戶需求
- 設計:因應客戶需求規劃API設計,或改善現有設計
- 製作原型:製作原型API(Prototype API) 或擬真API(Mock API) 供大家使用,
加速取得反饋並加以改進
- 交付:根據圓形與設計、測試、文件三項工作同時開發.不追求一次性完成,而是迭代優化循環,才可以逐步完成交付進度。
- 上線:讓內外部用戶使用API進行串接,良好的客戶支援此階段中最重要。
Domain Model 轉為 Web API 設計 (ADDR)
API設計會經歷四個階段,使用七個步驟來進行
目的
- 最終交付的產品能解決客戶問題,並且API設計符合客戶認知
- 終結混亂的設計流程,減少無謂的設計變化與重構
- 透過系統性的方法提高交付的效率
- 透ㄍㄨㄛˋ流程內的機制,讓技術/非技術方都有表達意見的機會,最終形成彼此共識
- 流程中的每個階段產出的文件、紀錄,都是有系統、可追溯、可傳承,而不是像白板上臨時起意的塗鴉讓人摸不著頭緒
四個階段
- Align(對齊):確保商業、產品、技術團隊對產品範圍與目的都有共同且一致的認知
- Define(定義):將商業與客戶需求轉成對應的數位能力,成為API實作的基礎
- Design(設計):依照特定的步驟制定API社既文件,並選定適合的API風格
- Refine(優化):根據用戶回饋優化API設計、文件、原型、測試
七個步驟
- 鑑別數位能力:釐清用呼需求,定義出能解決需求的數位能力
- 產生活動與步驟:從用戶的動機、需求、與情境中
找出具體的步驟
並將步驟所對應的數位能力納入API設計中 - 界定API邊界:將API依照相關性分們別類,以及確認是否可以用計有API滿足客戶需求,縮短交付時間
- 建立API模型:建立API模型,產出API Profile作為後續高階設計的基礎
- 高階設計:選擇適合的API實現風格,製作高階設計文件
- 優化設計:蒐集API用戶反饋改進產品
- 撰寫API文件:包括API歸格文件與入門指南,方便用戶進行串接
第三章
鑑別數位能力
企業組織提供自動化機制的能力模型
商業能力往往用KPI或OKR等指標來追蹤,但數位能力重視的是成果的產出,以期為了獲得商業能力所必要的行動。
商業能力表示的是組織的「What」,而數位能力表示的是組織的「How」。
JTBD (Job to be done)
藉由目標導向API才是真正對用戶有意義。
Job Story
挑戰一:太多細節
故事主要在於抽象的行動,細節可以用「附加細節區」,跟寫程式的抽象、介面的方法一樣,找出相同的動作
而不是實作細節。
挑戰二:太偏向功能面
一樣要抽象,不要專注在功能面
第四章
產生活動與步驟
從第三章的上圖第4點,「找到一兩本想買的書、下單、買到他然後等收貨」 是一個比較抽象的說法,但要如何把他拆解出來變成「產生活動」?
對於不熟悉的領域,我們需要尋求外部的協助,這時候就需要利用事件風暴
找出共識
利用事件風暴求出共識
這是一個團體活動,將一個未知的各個層面的商業流程、需求、事件等已具象化的方式呈現,透過一系列活動凝聚成員共識。主要增進的事我們對於特定領域的知識與共識,讓團隊具備通同的知識後,再帶回到各自的API之中。
事件風暴如何進行
這邊描述的應該有的幾個步驟、該如何進行、誰要參加、最終成果,但這個後續再來玩,太有趣了。
界定API邊界
API的反例
超級多合一API反模式
超肥的API讓開發者感到疑惑與挫折,太過於細碎也會有一樣的狀況。這就太過於極端不會犯這種錯誤.
超載API 反模式
一隻超完美的Accounts API 提供完整的帳戶資訊,或一隻完美的Customer API提供用戶完整資訊,理想很豐滿、現實很骨感,這種完美個API都不會有好下場。
書其實就是產品「Product」,應該區分用於分類使用、商品呈現使用、出貨用、訂單用,都各有自己的重要使命。這樣的API區分出來以後,可預見分類或是產品、購物車用的API的Request需求大於訂單跟出貨,又可以把它拆解出來。
用事件風暴界定邊界
跟之前學習到的DDD 一樣把這些行為模式列舉出來之後,就會找到很多相關詞,把這些相關詞列出來之後,就可以找到他的邊界
這邊提供兩個方法來界定他的邊界
使用共同詞彙
使用聚合
作為邊界
要注意使用聚合作爲依據可能會因為範圍的顆粒度不同,導致每個API的精細度不同,所以要注意
第六章 建立API模型
好的API設計來自於 API模型
,API模型協助制訂一個API的範圍與用途
、搞懂與驗證Client端與外部開發者的需求,跟畫面的Prototype不一樣(滿足客戶),API模型滿足開發者
、Client
的操作感受盡管兩者的需求是共同的但還是會有些差異,透過API建模的過程,能找出差異並建立解決方案,避免在錯誤的基礎上蓋房子。
建模API的素材來自於前面幾張,活動、步驟、工作故事,彙整過後就是API Profile
。API Profile 包含 一切特性,有名稱
、範圍
、事件
,有API模型的存在有機會告早在模型中發現問題並加以修正,相較於重構的成本
真的太划算了。
API Profile 結構
一份典型的 API Profile包含有以下項目
- API的名稱與說明
- API的存取範圍(Internal、Public、partner..等)
- API的操作及輸出入的詳細參數與格式
- API操作的可存取人員名單,品開放白名單內人員調用,避免安全隱患
- API操作具有的事件
- (選填)額外的標準或規範要求,例如 SLA
建議是可以共同協作的線上文件、Wiki...等等
為何不使用OpenAPI(SWagger)來當作API模型文件?
他是屬於讀取程式碼的REST或gRPC的規格書,他用途是API技術參考文件與範例程式,但是他會是後續建立API profile的好的素材。
第一步:建立 API profile概要
定義基本資料,包括名稱
、說明
、範圍(internal、public、partner)
等等,命名方式建議使用 lowerCamelCase,這對後續的時序圖會有幫助。
第二步:找出相關資源
找出跟PAI操作有關的事物,API的每個操作都會調動到的事物,聽起來很抽象,但其實之前我們已經對齊(Align)很定義(Define)階段就已經整理過了。
其中的Books、Cart, 這些之前就已經定義清楚了。
標示資源為何不直接拿DB的欄位作為對應?
- 資料庫設計注重結構與效能設計(正規化/反正規化),API層注重的是用戶導向並不是讀寫的效能,因此不建議拿DB Table作為對應資源
- 思考應該由高至低,如果從Table 開始會太多細節在裡面。
第三部:定義資源階層
資源間的層級關係(taxonomy)制定出來,所謂的taxonomy
是一個分類與組織事務的術語,用來表示階層資源間彼此的層次與關聯性。
資源關係有三種表達方式:
- Independent(獨立的):資料獨立存在,與其他資源不互相依賴
- Dependent(依賴的):一個資源必須依賴其他資源才能存在,父子概念(子無法單獨存在)。(依賴跟參考是不同的概念:如果兩個獨立的(Independent)互相參照並不表示他有依賴性,他們仍然是各自獨立)(如下圖範例)
- Associative(關聯的):兩個獨立的資源,而兩者間又需要建立起關係才能完整表達事務的全貌,這種狀況或加入一個中介資源來捕捉他們的關係,這種結構下那兩個互相獨立的資源的關係稱為associative。
下圖 Book資源
跟 Book Author 資源
是各自獨立的,但又可以互相參考讓事情全貌更完整。
上圖中有一個書籍的數量該放哪呢?
,就如下圖,當一本書被放進購物車那書的數量
、金額
應該怎麼放呢? 引入另一個資源CartItem來存放這些購物車的閔項細節。
因為引入CartItem,為了讓名稱一致,原本操作addBookToCart()
改成 AddItemToCart()
、removeBookFromCart()
改為removeItemCart()
加入事件操作
確認階層過後補完每個API操作具有的事件,API操作發送出的事件可以為API客戶端所使用
,例如資料分析、客戶端收到事件後也能在對API發起別的請求
此處的事件可以參考 事件風暴中的那些事件,做事件風暴的時候有定義有哪些事件,如果你未曾使用事件風暴,也可以用之前建模時所設定的那些事件。
事件的命名請使用過去式
,除此之外如果組織內既有的命名原則應該遵循
補充細節
我們將填入每一個操作的參數細節,包括他的
- 輸入參數
- 輸出格式,但並不要求填滿完整細節,這邊填寫
重點即可
,也可以預留空格。 - 同步(synchronous)或非同步(asynchronous)的資訊,這會影響後續設計階段的作業,同步API最基本一筆請求/一筆回復這樣來回拋接的作業模式,非同步 API會將請求放置
背景處理
。 - 安全性:不會對資源做出
狀態變更
的動作,對於讀取資源(GET)類的方法應該都要是safe的 - 不安全(unsafe):會改變
資源狀態
且重複操作也不保證會得到相同結果
,多半出現在(PUT和PATCH) - 冪等操作:具有冪等性的話,可以讓客戶不用擔心取代資源或是刪除資源(PUT與DELETE)
用時序圖驗證API模型
確保API作業如預期,我們使用時序圖來驗證
並且拿此圖來跟大家討論API
作業的意見。
確認API profile的特性是否跟工作故事的特性一致、沒有做歪。不是畫圖就結束,我們要邊畫邊模擬真實的互動情境,設想每個操作,資源是不是符合預期,中間是否有缺漏,如果有請回到建模步驟再次補完。
驗證完畢後將API模型分享給大家,包括技術
和非技術
、外部專家
、早期用戶
等,徵求反饋,進入設計階段前,務必要確保目前的成果是大家一致認可的。
sequenceDiagram participant Customer participant Shopping API participant Order Creation API participant Payment Processing API Customer->>Shopping API: listBooks() Shopping API-->>Customer: Books[] Customer->>Shopping API: addBookToCart(bookId) Shopping API-->>Customer: Cart Customer->>Shopping API: removeBookFromCart(bookId) Customer->>Shopping API: searchBooks(searchQuery) Shopping API-->>Customer: Books[] Customer->>Shopping API: addBookToCart(bookId) Shopping API-->>Customer: Cart Customer->>Shopping API: viewCart() Shopping API-->>Customer: Cart Customer->>Order Creation API: createOrderFromCart(cartId) Order Creation API-->>Customer: Order Customer->>Payment Processing API: payForOrder(orderId) Payment Processing API-->>Customer: PaymentDetails
評估API優先順序與重用性
- 評估API提供的商業與市場價值,試問自己幾個問題
- API在市場上有競爭優勢嗎?
- API能取代過去的人工作業嗎? 能省下過往的人工成本嗎?
- API能開闊新的收入管道嗎?或是增加原有的收入管道?
- API能帶來哪方面的商業效益呢?是BI(商業智慧)?市場洞察?或是決策因子?
- API帶來的自動化效益可以讓企業組織的核心業務做到更多更好嗎?
以上答案皆非,那不用做了,如果有正面的肯定答案就表示可以做了。
- 再來評估每隻API的難度,API的難度可以用在事件風暴所產生的步驟來估計,越多步驟、複雜、時間,難度就越高,反之亦然,可區分 低、中、高。
- 看看現有的資源是否有可以拿來使用修改,不需要自己重新造輪子
總結
- 資源的階層(taxonomy):表示資源間的關係是否互相依賴
- API profile:API高階設計文件,不涉及特定設計風個或實作技術
- 時序圖:驗證API模型是否與工作故事相一致
- 評估優先度:確保API的存在價值
了解整體核心價值後,可以把非核心業務由外部API處理(或初階工程師),或沿用現有的,找到平衡點。
第七章 REST API
REST 被設計適用於
粗顆粒度
的訊息交換,儘管這是Web最普遍的形式,但對於其他形式,REST就不是最佳解。
REST是一來一往的過程,並不能像 RPC可以達到非同步也沒有結構性約束,頁面上的操作往往是比較粗的呈現(適用REST),想要更細的操作(RPC這類就不錯)
沒有特別需要細粒度的需求,一班都會選擇REST作為主要API風格
REST從來就不是CRUD + JSON,是一系列的架構性約束與約定,這些約束與約定制定了一個系統的模組間該如何有效的工作與互動
REST的架構性約束給系統更好的靈活性(flexibility)與進化性(evolvability)
- 主從式架構:服務端與客戶端互相獨立,兩者之間用請求/回覆的機制進行訊息互動。
- 無狀態:服務端不會記憶客戶端的狀態,所以每次客戶端發送請求時都要附上用戶的驗證資訊。注意:服務端自身的狀態式會儲存的。
- 分層式系統:對客戶端來說服務端就像是黑盒子,但服務端的角度來看黑盒子的內部式許多的子模組堆疊起來,每一層負責的任務不同,例如:快取、反向代理、認證...等等。
- Code on demand(選配):客戶端的程式碼可以來自服務端,可能是腳本也可能是二進位程式,瀏覽器跑Web APP就是Client、API可能是SDK。客戶端只負責執行環境,商業邏輯由服務端掌控,改版時也不西澳客戶端搭配升級。
- 一致的介面:REST得以上架構性約束,讓API對外形成一致的介面。
- Hypermedia特性:除了原本的資料,還可以挾帶其他資訊(這就稱為:Hypermedia),讓客戶端了解更多可能性、額外操作、狀態等等,這樣可以避免
客戶端帶來頻繁的操作影響效能
,典型案例就是分頁的元素。
從這邊來看,下一頁、上一頁這類的已經把資料產生好並且直接給連結,這樣可以做好快取讓使用者來讀取這頁的快取資料,而不是直接呼叫API來動態產生資料消耗資料庫資源
- HATEOAS (hypermedia as the engine of application state) :回覆資料提供額外的資訊,甚至可以根據狀態提供Client可以採取的動作(上一頁、下一頁,編輯/佔存、同意/拒絕)
REST API 設計流程
第一步:設計資源的URL路徑
根據6.7的圖片,已經列出所有資源路徑
!200-Areas/230-知識擴展/讀書筆記/WEBAPI設計原則/resource/WEBAPI設計原則-29.png
對於關聯式資料庫熟悉的人可會傾向用資料庫的角度去規劃資源的相關性,這可能導致資源關聯性過於複雜
,大原則還是用戶導向
,不影響API用戶認知的前提下,盡可能讓URL精簡,只留下較具有顯著意義的關聯資源。
!200-Areas/230-知識擴展/讀書筆記/WEBAPI設計原則/resource/WEBAPI設計原則-30.png
傾向複數表示某種資源的集合
!200-Areas/230-知識擴展/讀書筆記/WEBAPI設計原則/resource/WEBAPI設計原則-32.png
第二步:將API操作對應到HTTP方法
根據 API Profile 跟 HTTP 方法安全特性 如下圖,可以結合出每項操作的HTTP方法
!200-Areas/230-知識擴展/讀書筆記/WEBAPI設計原則/resource/WEBAPI設計原則-31.png
!200-Areas/230-知識擴展/讀書筆記/WEBAPI設計原則/resource/WEBAPI設計原則-33.png
!200-Areas/230-知識擴展/讀書筆記/WEBAPI設計原則/resource/WEBAPI設計原則-35.png
設定回應碼
200:成功,或與成功相關
400:失敗,意味著客戶端必須修正他的請求並載次嘗試
500:服務端異常,或因為客戶端導致的服務端異常
不要發明回應碼
,尊重業界慣例,下圖是加上回應碼後的表格。
!200-Areas/230-知識擴展/讀書筆記/WEBAPI設計原則/resource/WEBAPI設計原則-40.png
第四步:撰寫 REST API 設計文件
!200-Areas/230-知識擴展/讀書筆記/WEBAPI設計原則/resource/WEBAPI設計原則-38.png
可以是用自己喜歡的方式,本書用 Swagger Editor 作為範例 Swagger Editor
在Swagger Editor中,以API Profile內的資料為基礎,填入API名稱等基本資訊,在Description中寫入API的用途、功能、說明、操作等端點資訊,並註明和本API相關的其他API資料,但不要寫到API內部運作細節,細節應該由Wiki工人調閱。
!200-Areas/230-知識擴展/讀書筆記/WEBAPI設計原則/resource/WEBAPI設計原則-41.png
!200-Areas/230-知識擴展/讀書筆記/WEBAPI設計原則/resource/WEBAPI設計原則-42.png
!200-Areas/230-知識擴展/讀書筆記/WEBAPI設計原則/resource/WEBAPI設計原則-44.png
!200-Areas/230-知識擴展/讀書筆記/WEBAPI設計原則/resource/WEBAPI設計原則-45.png
接著用時序圖模擬真實的互動狀況,驗證目前的設計有無背離當初的作故事與事件風暴的規劃,驗證的時序圖如下,展示了用戶購物情境
!200-Areas/230-知識擴展/讀書筆記/WEBAPI設計原則/resource/WEBAPI設計原則-46.png
第五步:分享並獲得回饋
將設計分享出去,可以分享給團隊內的同事,盡早分享給早期用戶並蒐集他們的反饋。
因為API發佈出去就覆水難收,端點操作、參數都難以在改變,串好的應用也不可能接受破壞性的改變
,唯一能做的只有追加新功能或新特性
,在發佈API前務必檢討再檢討,分享出去給成員們請求協助審閱,找出問題,鼓勵回饋意見,讓API發佈前就已經過千錘百鍊,而不是亡羊補牢。
除此之外也可以利用Mock API 讓人們去測試API實際行為,也可以驗證資料的正確性,相較於眼睛看,手動可以獲得更好的體驗。
決定API的表現格式
格式選擇原則不要三心二意,一定決定後就盡量不要更改,讓每一套都一致減少後面不必要的複雜度。
隨著時間眼鏡難免需要提供別得格式來回應市場需求,為了避免破壞現有串接,格式的遷移應該是漸進式的,如果要同事供應多種格式,那需要利用HTTP的內容協商機制,他讓客戶端可以指定回應的格式,API再據此回應之。
下表格是循序漸進的,從最基本的序列化開始,逐漸加上jypermedia等其他特性,當然複雜度也隨之增高
!200-Areas/230-知識擴展/讀書筆記/WEBAPI設計原則/resource/WEBAPI設計原則-47.png
資源序列化
說白就是JSON、XML、YAML這邊沒什麼好說的,反而是後面的說明觀察它的變化比較是關注的要點
!200-Areas/230-知識擴展/讀書筆記/WEBAPI設計原則/resource/WEBAPI設計原則-49.png
序列化加 Hypermedia
Hypermedia的序列化與前面的資源序列化類似,只不過加上hypermedia的額外資訊,除了用連結表示,也可以把其他資源直接置入回應中,這種模式稱為內嵌資源(embedded resource)
。
可以用一個格式 HAL(Hypertext Application Language)的格式作為hypermedia的序列化格式,但實際HAL也是基於JSON的基礎再去語意擴充的格式,這樣好處是改版挾帶hypermedia也不會影響原有的客戶端串接
!200-Areas/230-知識擴展/讀書筆記/WEBAPI設計原則/resource/WEBAPI設計原則-50.png
支援Hypermedia的通用訊息格式
Hypermedia在互動訊息方面,尋求統一個標準語法或結構的格式,該格式必須要能夠用於傳達所需要的資源、屬性、連結等資訊,才能讓用戶端能以更通用的方式解析這些資訊。
有統一互動格式,前後端才有共同語言像是打通任督二脈
hypermedia的序列化格式有JSON:API
和Siren
他們都符合標準化的語法跟結構。
語意化的通用訊息格式
語意化(semantic) 是個牽涉廣泛議題,主流採用Schema.org的方案,以下示範立,雖然變胖了但是換來讓客戶端讀懂內容,例如提供更有效率的UI/UX、更動態的頁面元素、頁面跳轉、更輕量化的客戶端....等等。
!200-Areas/230-知識擴展/讀書筆記/WEBAPI設計原則/resource/WEBAPI設計原則-53.png
!200-Areas/230-知識擴展/讀書筆記/WEBAPI設計原則/resource/WEBAPI設計原則-52.png