API 設計的關鍵:精準界定 API 邊界

更新於 發佈於 閱讀時間約 18 分鐘

大家好!今天我們要討論的是 API 設計中的一個關鍵步驟——界定 API 邊界。這個過程就像是在建造一個圖書館之前,先確定每個區域的用途:哪些地方是放書的、哪些地方是供人閱讀的,以及哪些地方需要與其他部門協作。通過合理界定 API 的邊界,我們可以確保 API 的功能既精確又高效,避免陷入過度設計或欠缺設計的陷阱。


1.什麼是API邊界?

API 的邊界決定了它的功能範圍,就像圖書館的功能區劃一樣。清晰地界定 API 的邊界,不僅能夠幫助你專注於 API 的核心功能,還能避免捲入不必要的複雜性和業務耦合,從而提高系統的靈活性和可維護性。

我們先來反向思考一下:如果 API 沒有明確的邊界,會變成什麼樣子?

沒有邊界的 API:多合一反模式

假設今天你要為公司開發一個客戶管理系統,這個系統需要對公司客戶進行 CRUD 操作(Create, Read, Update, Delete),具體需求包括:

  1. 查詢單筆客戶
  2. 查詢多筆客戶
  3. 新增客戶
  4. 修改單筆客戶
  5. 批次修改客戶
  6. 刪除單筆客戶

如果我們不清楚地界定 API 的邊界,而是選擇一個「多合一」的 API 來處理這些需求,那麼 API 可能會設計成這樣:

public class Customer
{
public string Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}

public class ApiResult
{
public bool Success { get; set; }
public string Message { get; set; }
public List<Customer> Customers { get; set; }
public Customer SingleCustomer { get; set; }
}

public ApiResult CustomerAction(List<Customer> customers, string actionType)
{
switch (actionType)
{
case "GetSingle":
// 查詢單筆客戶邏輯
break;
case "GetMultiple":
// 查詢多筆客戶邏輯
break;
case "Create":
// 新增客戶邏輯
break;
case "UpdateSingle":
// 修改單筆客戶邏輯
break;
case "UpdateBatch":
// 批次修改客戶邏輯
break;
case "Delete":
// 刪除客戶邏輯
break;
default:
return new ApiResult { Success = false, Message = "無效的操作類型" };
}

return new ApiResult { Success = true, Message = "操作成功" };
}

或許部分初學者會覺得這不就是switch case 的用法,根據Type來分類並執行他的動作,但若是把詳細的各動作邏輯都寫出來,程式碼的閱讀性會變的非常糟糕。不妨看看下面這個將邏輯部分完整呈現的範例:

public class Customer
{
public string Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}

public class ApiResult
{
public bool Success { get; set; }
public string Message { get; set; }
public List<Customer> Customers { get; set; }
public Customer SingleCustomer { get; set; }
}

private List<Customer> _customers = new List<Customer>();

public ApiResult CustomerAction(List<Customer> customers, string actionType, string customerId = null)
{
switch (actionType)
{
case "GetSingle":
var customer = _customers.FirstOrDefault(c => c.Id == customerId);
if (customer == null)
{
return new ApiResult { Success = false, Message = "客戶不存在" };
}
return new ApiResult { Success = true, Message = "查詢成功", SingleCustomer = customer };

case "GetMultiple":
return new ApiResult { Success = true, Message = "查詢成功", Customers = _customers };

case "Create":
foreach (var newCustomer in customers)
{
_customers.Add(newCustomer);
}
return new ApiResult { Success = true, Message = "新增成功" };

case "UpdateSingle":
var existingCustomer = _customers.FirstOrDefault(c => c.Id == customerId);
if (existingCustomer == null)
{
return new ApiResult { Success = false, Message = "客戶不存在" };
}
existingCustomer.Name = customers[0].Name;
existingCustomer.Email = customers[0].Email;
return new ApiResult { Success = true, Message = "更新成功" };

case "UpdateBatch":
foreach (var updatedCustomer in customers)
{
var customerToUpdate = _customers.FirstOrDefault(c => c.Id == updatedCustomer.Id);
if (customerToUpdate != null)
{
customerToUpdate.Name = updatedCustomer.Name;
customerToUpdate.Email = updatedCustomer.Email;
}
}
return new ApiResult { Success = true, Message = "批次更新成功" };

case "Delete":
var customerToDelete = _customers.FirstOrDefault(c => c.Id == customerId);
if (customerToDelete == null)
{
return new ApiResult { Success = false, Message = "客戶不存在" };
}
_customers.Remove(customerToDelete);
return new ApiResult { Success = true, Message = "刪除成功" };

default:
return new ApiResult { Success = false, Message = "無效的操作類型" };
}
}

看到這一大陀的程式碼,如果要針對特定的功能進行測試與修改,你要先能一眼找出要修改的點變的非常困難,更何況上述還不是很完整的處理商業邏輯,還是非常省略過後的程式。因此我們能看到

沒有邊界的 API 帶來的問題

這種多合一的 API 設計看起來很方便,所有的操作都集中在一個方法中。然而,這種設計會帶來以下問題:

  1. 可讀性差: 隨著功能的增多,CustomerAction 方法的代碼將變得越來越長,難以維護。每次新增或修改功能,都需要改動這個方法,容易引入錯誤。
  2. 高耦合度: 不同的業務邏輯被耦合在一起,任何一個操作的改變都可能影響到其他操作,導致代碼的穩定性下降。
  3. 難以測試: 由於所有操作都集中在一個方法中,對這個方法進行單元測試時需要考慮多種情況,測試複雜度大大增加。
  4. 無法擴展: 當你需要為客戶管理系統新增更多功能時,這個多合一 API 的複雜性會成倍增長,最終導致代碼難以擴展。

有邊界的 API 設計:分而治之

為了解決上述問題,我們需要明確 API 的邊界,將不同的操作分散到不同的 API 方法中。這樣,每個 API 只負責一項具體的功能,既清晰又易於維護。

public ApiResult GetCustomerById(string id)
{
// 查詢單筆客戶邏輯
}

public ApiResult GetCustomers()
{
// 查詢多筆客戶邏輯
}

public ApiResult CreateCustomer(Customer customer)
{
// 新增客戶邏輯
}

public ApiResult UpdateCustomer(Customer customer)
{
// 修改單筆客戶邏輯
}

public ApiResult UpdateCustomers(List<Customer> customers)
{
// 批次修改客戶邏輯
}

public ApiResult DeleteCustomer(string id)
{
// 刪除單筆客戶邏輯
}

有邊界的 API 帶來的好處

  1. 清晰的結構: 每個 API 方法只負責一項具體任務,代碼結構清晰,易於閱讀和理解。
  2. 低耦合度: 不同的業務邏輯被分開,修改某一個功能時,不會影響到其他功能,代碼更穩定。
  3. 易於測試: 由於每個 API 方法只負責一個功能,測試變得簡單,能夠專注於測試單一功能的正確性。
  4. 良好的可擴展性: 當需要新增功能時,可以輕鬆地添加新的 API 方法,而不會影響到現有的功能。

上面的程式範例,還不是主流的架構設計下的程式碼範例,帶篇幅慢慢帶到,會一步一步將程式碼修正到較好的架構與設計。


2. 如何界定 API 邊界?

界定 API 邊界涉及多個步驟,從理解業務需求到識別核心資源,再到劃分功能責任。這些步驟幫助我們構建一個精確且靈活的 API,能夠滿足業務需求而不會過度膨脹。

  1. 理解業務需求: 首先,你需要深入理解 API 所服務的業務領域。這意味著你必須清楚地了解業務流程,以及哪些功能是 API 必須支持的。例如,在圖書館管理系統中,圖書的借閱和歸還是核心功能,因此這部分必須由 API 來處理。
  2. 識別核心資源: 核心資源是 API 必須管理的主要對象,這些資源通常是業務中最重要的實體。在圖書館管理系統中,核心資源可能包括「圖書」、「用戶」和「借閱記錄」。這些資源的管理需要被 API 清晰地界定和實現。
  3. 劃分功能責任: 明確哪些功能應該由 API 來處理,哪些應該留給其他系統或模組來完成。例如,圖書館的書籍管理和借閱系統可以由 API 處理,但身份驗證和支付功能可能應該交給專門的第三方服務來負責。這種劃分可以讓 API 聚焦於核心業務,同時保持整個系統的靈活性和可擴展性。
  4. 正確的HTTP 規格:
    正確使用 HTTP 方法和狀態碼是界定 API 邊界的重要部分。遵循 RESTful 設計原則,我們可以更清晰地定義 API 的功能和行為:
    同時,使用適當的 HTTP 狀態碼可以更好地表達 API 的響應:
    通過正確使用 HTTP 方法和狀態碼,我們可以更好地定義 API 的邊界,使其行為更加清晰和可預測。
    • GET: 用於獲取資源,不應該對資源進行修改。例如,獲取圖書信息或借閱記錄。
    • POST: 用於創建新資源。例如,添加新的圖書或創建新的借閱記錄。
    • PUT: 用於更新現有資源。例如,更新圖書信息或修改借閱記錄。
    • DELETE: 用於刪除資源。例如,刪除過期的借閱記錄。
    • PATCH: 用於部分更新資源。例如,更新圖書的部分信息而不是全部。
    • 200 OK: 請求成功。
    • 201 Created: 資源創建成功。
    • 204 No Content: 請求成功,但無返回內容(如刪除操作)。
    • 400 Bad Request: 請求格式錯誤。
    • 404 Not Found: 請求的資源不存在。
    • 500 Internal Server Error: 服務器內部錯誤。

3. 案例分析:圖書館管理系統的 API 邊界

假設你正在設計一個圖書館管理系統的 API,我們可以通過以下方式來界定它的邊界:

  • 應該處理的功能:
    • 圖書管理: API 應該負責圖書的新增、查詢、更新和刪除。這些操作直接關係到圖書館的核心業務——書籍管理。
    • 借閱管理: API 還應該負責處理書籍的借閱和歸還,包括借閱記錄的維護,這是圖書館運作的關鍵部分。
  • 不應該處理的功能:
    • 身份驗證: 這部分應該由專門的身份驗證系統處理,API 只需接收來自身份驗證系統的驗證結果並據此進行操作。
    • 支付處理: 如果涉及到罰款或其它支付項目,應該由第三方支付服務來處理,API 只需與該支付系統進行集成,處理支付結果即可。

這樣的劃分可以幫助 API 專注於圖書館的核心業務,而不會因為捲入過多的非核心功能而變得複雜難維護。


總結:

界定 API 邊界是設計高效且可維護 API 的第一步。通過明確 API 的職責範圍,我們可以確保它專注於核心業務,並且能夠靈活應對未來的需求變更。這種精確的劃分可以避免過度設計和欠缺設計,從而提高整個系統的穩定性和可擴展性。明天,我們將在此基礎上進一步討論如何建立 API 模型,這將為 API 的實際開發奠定堅實的基礎。

avatar-img
0會員
12內容數
歡迎來到 ChiYu Code Journey!這裡是我分享技術心得與開發經驗的空間,主要內容涵蓋 C#、.Net、API 開發及雲端等程式主題。偶爾也會分享一些日常生活點滴,像是我與我家可愛的法鬥相處的趣事等。希望在這裡能和大家一起學習、交流,一同踏上這段程式旅程!
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
ChiYu Code Journey 的其他內容
本文深入探討 Web API 與 HTTP 協議,解釋 HTTP 請求方法 (GET, POST, PUT, PATCH, DELETE)、HTTP 結構 (Headers, Body, 狀態碼),。透過說明各種 HTTP 狀態碼,讀者可以更深入理解 Web API 的設計與應用。
本篇文章淺顯易懂地介紹什麼是API(應用程式介面),並以生活化的例子和C#程式碼範例說明介面的概念,以及API在不同領域的應用和優勢,例如Web API、作業系統API、庫或框架API等,並點出其在社群媒體整合、支付系統、地圖服務等日常生活中的重要性。
本文深入探討 Web API 與 HTTP 協議,解釋 HTTP 請求方法 (GET, POST, PUT, PATCH, DELETE)、HTTP 結構 (Headers, Body, 狀態碼),。透過說明各種 HTTP 狀態碼,讀者可以更深入理解 Web API 的設計與應用。
本篇文章淺顯易懂地介紹什麼是API(應用程式介面),並以生活化的例子和C#程式碼範例說明介面的概念,以及API在不同領域的應用和優勢,例如Web API、作業系統API、庫或框架API等,並點出其在社群媒體整合、支付系統、地圖服務等日常生活中的重要性。
你可能也想看
Google News 追蹤
Thumbnail
/ 大家現在出門買東西還會帶錢包嗎 鴨鴨發現自己好像快一個禮拜沒帶錢包出門 還是可以天天買滿買好回家(? 因此為了記錄手機消費跟各種紅利優惠 鴨鴨都會特別注意銀行的App好不好用! 像是介面設計就是會很在意的地方 很多銀行通常會為了要滿足不同客群 會推出很多App讓使用者下載 每次
Thumbnail
※ 什麼是Web API API 就是後端開出來讓前端來用的介面,讓前端與後端可以溝通。 API流程: 終端使用者用任何一種裝置進入瀏覽器。 瀏覽器透過 API 向後端發出請求,請求查詢或修改資料。 後端透過 API 收到前端的請求後,取得資料並回應給前端。 前端渲染畫面,終端使用者
Thumbnail
需求情境: 在設計畫面時,資料來源是後台的 api,每一次畫面細節的修修改改,都會觸發 Xcode Preview 程序,導致不斷呼叫後台。此時若資料結構和大小都具有一定規模,就會導致效率低落,不斷等待,且消耗伺服器資源甚鉅。 解決方案: 將後台傳回的資料以檔案形式暫存在本地端,每次 pr
Thumbnail
本書介紹了戰略設計、管理領域複雜度、實際應用領域驅動設計等主題。透過對核心子領域、支持子領域、限界上下文等概念的探討,提供了領域驅動設計的相關知識。這篇文章中還涉及了微服務、事件驅動架構和資料網格等相關主題,提供了設計系統和應用領域驅動設計的指導。
Thumbnail
軟體系統的發展歷程大多相似,首重解決基本需求、提供操作介面,進而提升安全性、擴充功能、優化操作。
※ 什麼是 RESTful API? 這種運用 HTTP 來表達語義的路由設計風格稱為 RESTful API,它描述了如何實現 Web API 的架構。所謂的 API 是應用程式介面 (application programming interface),網址也是一種應用程式的「介面」,故稱為
Thumbnail
當我們在撰寫一套系統的時候, 總是會提供一個介面讓使用者來觸發功能模組並回傳使用者所需的請求, 而傳統的安裝包模式總是太侷限, 需要個別主機獨立安裝, 相當繁瑣, 但隨著時代的演進與互聯網的崛起, 大部分的工作都可以藉由網頁端、裝置端來觸發, 而伺服端則是負責接收指令、運算與回傳結果, 雲端
Thumbnail
當這產品的這個 API 被呼叫,再從回傳內容的某個欄位欄位來判斷,只要“這個欄位”顯示 false 就代表不支援」,雖然這樣的設計也能滿足功能需求…
Thumbnail
系統的分析與規劃 在談到程式設計時,首要的是進行系統的分析與規劃。程式設計的起點通常是系統分析與規劃,這涉及到如何分析和設計系統的大原則和方向。為了達到預期效果,重要的是擁有對產業的清晰邏輯認識和深入了解。 進行深入了解 若要進行系統分析,必須對企業的設計和程式設計的對象進行深入了解,以充分理
Thumbnail
先前幾篇筆記介紹了網路請求,瀏覽器儲存資料的方式,那麼實務上,前端最常需要發送網路請求的時候,就是透過呼叫 API,去向後端工程師發送/請求資料,所以今天來記錄什麼是 API吧!
Thumbnail
提到後端工程師,似乎就只是開發 API,但一個複雜的系統其實不太可能只透過 API 就能完成,例如一個簡單的功能,註冊會員,其實是由好幾個不同類型的工作互相配合,您才能收到開通信,才確保資料庫不會有一堆未開通帳號等。所以今天就來聊聊一個系統有幾種不同執行方式的工作。
Thumbnail
/ 大家現在出門買東西還會帶錢包嗎 鴨鴨發現自己好像快一個禮拜沒帶錢包出門 還是可以天天買滿買好回家(? 因此為了記錄手機消費跟各種紅利優惠 鴨鴨都會特別注意銀行的App好不好用! 像是介面設計就是會很在意的地方 很多銀行通常會為了要滿足不同客群 會推出很多App讓使用者下載 每次
Thumbnail
※ 什麼是Web API API 就是後端開出來讓前端來用的介面,讓前端與後端可以溝通。 API流程: 終端使用者用任何一種裝置進入瀏覽器。 瀏覽器透過 API 向後端發出請求,請求查詢或修改資料。 後端透過 API 收到前端的請求後,取得資料並回應給前端。 前端渲染畫面,終端使用者
Thumbnail
需求情境: 在設計畫面時,資料來源是後台的 api,每一次畫面細節的修修改改,都會觸發 Xcode Preview 程序,導致不斷呼叫後台。此時若資料結構和大小都具有一定規模,就會導致效率低落,不斷等待,且消耗伺服器資源甚鉅。 解決方案: 將後台傳回的資料以檔案形式暫存在本地端,每次 pr
Thumbnail
本書介紹了戰略設計、管理領域複雜度、實際應用領域驅動設計等主題。透過對核心子領域、支持子領域、限界上下文等概念的探討,提供了領域驅動設計的相關知識。這篇文章中還涉及了微服務、事件驅動架構和資料網格等相關主題,提供了設計系統和應用領域驅動設計的指導。
Thumbnail
軟體系統的發展歷程大多相似,首重解決基本需求、提供操作介面,進而提升安全性、擴充功能、優化操作。
※ 什麼是 RESTful API? 這種運用 HTTP 來表達語義的路由設計風格稱為 RESTful API,它描述了如何實現 Web API 的架構。所謂的 API 是應用程式介面 (application programming interface),網址也是一種應用程式的「介面」,故稱為
Thumbnail
當我們在撰寫一套系統的時候, 總是會提供一個介面讓使用者來觸發功能模組並回傳使用者所需的請求, 而傳統的安裝包模式總是太侷限, 需要個別主機獨立安裝, 相當繁瑣, 但隨著時代的演進與互聯網的崛起, 大部分的工作都可以藉由網頁端、裝置端來觸發, 而伺服端則是負責接收指令、運算與回傳結果, 雲端
Thumbnail
當這產品的這個 API 被呼叫,再從回傳內容的某個欄位欄位來判斷,只要“這個欄位”顯示 false 就代表不支援」,雖然這樣的設計也能滿足功能需求…
Thumbnail
系統的分析與規劃 在談到程式設計時,首要的是進行系統的分析與規劃。程式設計的起點通常是系統分析與規劃,這涉及到如何分析和設計系統的大原則和方向。為了達到預期效果,重要的是擁有對產業的清晰邏輯認識和深入了解。 進行深入了解 若要進行系統分析,必須對企業的設計和程式設計的對象進行深入了解,以充分理
Thumbnail
先前幾篇筆記介紹了網路請求,瀏覽器儲存資料的方式,那麼實務上,前端最常需要發送網路請求的時候,就是透過呼叫 API,去向後端工程師發送/請求資料,所以今天來記錄什麼是 API吧!
Thumbnail
提到後端工程師,似乎就只是開發 API,但一個複雜的系統其實不太可能只透過 API 就能完成,例如一個簡單的功能,註冊會員,其實是由好幾個不同類型的工作互相配合,您才能收到開通信,才確保資料庫不會有一堆未開通帳號等。所以今天就來聊聊一個系統有幾種不同執行方式的工作。