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
留言分享你的想法!
avatar-img
ChiYu Code Journey
0會員
12內容數
歡迎來到 ChiYu Code Journey!這裡是我分享技術心得與開發經驗的空間,主要內容涵蓋 C#、.Net、API 開發及雲端等程式主題。偶爾也會分享一些日常生活點滴,像是我與我家可愛的法鬥相處的趣事等。希望在這裡能和大家一起學習、交流,一同踏上這段程式旅程!
ChiYu Code Journey的其他內容
2025/01/23
在前一篇文章中,我們探討了非同步程式設計的基本概念,並介紹了如何使用 Task、Task<T>、async 和 await 來設計非同步操作。然而,非同步程式設計並非總是那麼直截了當。在實際開發中,開發者經常會遇到一些挑戰,這些挑戰主要來自於高併發、多執行緒以及非同步操作的特性。
2025/01/23
在前一篇文章中,我們探討了非同步程式設計的基本概念,並介紹了如何使用 Task、Task<T>、async 和 await 來設計非同步操作。然而,非同步程式設計並非總是那麼直截了當。在實際開發中,開發者經常會遇到一些挑戰,這些挑戰主要來自於高併發、多執行緒以及非同步操作的特性。
2025/01/22
在 C# 中,非同步程式設計是一種有效提升應用性能、併發處理能力的重要技術。今天,我們將詳細介紹 C# 中的非同步核心概念:async、await、Task 和 Task<T>,說明它們的使用方式,並探討一些進階應用。最後,我們會簡單介紹一些非同步程式設計中常見的問題。 什麼是非同步程式設計?
2025/01/22
在 C# 中,非同步程式設計是一種有效提升應用性能、併發處理能力的重要技術。今天,我們將詳細介紹 C# 中的非同步核心概念:async、await、Task 和 Task<T>,說明它們的使用方式,並探討一些進階應用。最後,我們會簡單介紹一些非同步程式設計中常見的問題。 什麼是非同步程式設計?
2025/01/21
這篇文章探討非同步編程的優缺點,並提供在設計非同步系統時需要注意的事項。非同步編程允許程式在等待 I/O 操作完成的同時,繼續執行其他工作,提高響應速度和資源利用率。然而,非同步程式設計也增加了系統複雜性,需要謹慎處理錯誤和確保代碼可讀性。
Thumbnail
2025/01/21
這篇文章探討非同步編程的優缺點,並提供在設計非同步系統時需要注意的事項。非同步編程允許程式在等待 I/O 操作完成的同時,繼續執行其他工作,提高響應速度和資源利用率。然而,非同步程式設計也增加了系統複雜性,需要謹慎處理錯誤和確保代碼可讀性。
Thumbnail
看更多
你可能也想看
Thumbnail
創作者營運專員/經理(Operations Specialist/Manager)將負責對平台成長及收入至關重要的 Partnership 夥伴創作者開發及營運。你將發揮對知識與內容變現、影響力變現的精準判斷力,找到你心中的潛力新星或有聲量的中大型創作者加入 vocus。
Thumbnail
創作者營運專員/經理(Operations Specialist/Manager)將負責對平台成長及收入至關重要的 Partnership 夥伴創作者開發及營運。你將發揮對知識與內容變現、影響力變現的精準判斷力,找到你心中的潛力新星或有聲量的中大型創作者加入 vocus。
Thumbnail
在現代網路與雲端架構中,負載平衡(Load Balancer)、橫向擴展(Scale Out)、以及 API 溝通機制是不可或缺的基礎。本文帶你快速理解負載平衡如何分散流量、系統如何透過擴展應對成長需求,以及 API 在不同服務間扮演的溝通角色。
Thumbnail
在現代網路與雲端架構中,負載平衡(Load Balancer)、橫向擴展(Scale Out)、以及 API 溝通機制是不可或缺的基礎。本文帶你快速理解負載平衡如何分散流量、系統如何透過擴展應對成長需求,以及 API 在不同服務間扮演的溝通角色。
Thumbnail
在產品開發過程中,PM 對技術概念的理解深度可能會影響需求落地精準度 與開發時程可控性,若能掌握一些基本技術用語,不僅能幫助 PM 更好地理解技術限制與實作可能性,更能提升與工程師的溝通效率。
Thumbnail
在產品開發過程中,PM 對技術概念的理解深度可能會影響需求落地精準度 與開發時程可控性,若能掌握一些基本技術用語,不僅能幫助 PM 更好地理解技術限制與實作可能性,更能提升與工程師的溝通效率。
Thumbnail
有效授權是管理者的關鍵能力。本文探討授權的基礎,包括管理系統的完善、適合的授權對象評估、權責一致性及九大授權條件。授權不僅能提高效率、釋放壓力,更能培養人才。文章也解析組織層級設計與八大功能分支,強調建立清晰授權結構的重要性,以避免混亂並促進企業穩定發展。
Thumbnail
有效授權是管理者的關鍵能力。本文探討授權的基礎,包括管理系統的完善、適合的授權對象評估、權責一致性及九大授權條件。授權不僅能提高效率、釋放壓力,更能培養人才。文章也解析組織層級設計與八大功能分支,強調建立清晰授權結構的重要性,以避免混亂並促進企業穩定發展。
Thumbnail
撰寫的API規格書是軟體PM必學技能,能有效提升開發效率並減少溝通誤差。本文分享API規格書從需求收集、設計架構到版本管理。PM不需寫程式,但需理解API邏輯,成為開發與需求方的溝通橋樑,讓專案更順暢、開發團隊更高效!
Thumbnail
撰寫的API規格書是軟體PM必學技能,能有效提升開發效率並減少溝通誤差。本文分享API規格書從需求收集、設計架構到版本管理。PM不需寫程式,但需理解API邏輯,成為開發與需求方的溝通橋樑,讓專案更順暢、開發團隊更高效!
Thumbnail
在軟體工程中,文件管理常被忽視,但對於多方協作至關重要。本文深入探討API文件、YAML格式和UML圖的應用,強調保持文件的版本控制,使用Swagger和Markdown工具提升可讀性與協作效率。此外,我們將討論如何利用PlantUML輕鬆繪製UML圖,以促進開發團隊之間的有效溝通與理解。
Thumbnail
在軟體工程中,文件管理常被忽視,但對於多方協作至關重要。本文深入探討API文件、YAML格式和UML圖的應用,強調保持文件的版本控制,使用Swagger和Markdown工具提升可讀性與協作效率。此外,我們將討論如何利用PlantUML輕鬆繪製UML圖,以促進開發團隊之間的有效溝通與理解。
Thumbnail
這本書大概花了一個禮拜的零碎時間看完,其實收穫很多,很多原則不僅僅適用於微服務,也適用在單體式應用被部署在很多節點上,加上跟過去的經驗比較,更能體會到書中的觀點
Thumbnail
這本書大概花了一個禮拜的零碎時間看完,其實收穫很多,很多原則不僅僅適用於微服務,也適用在單體式應用被部署在很多節點上,加上跟過去的經驗比較,更能體會到書中的觀點
Thumbnail
為了確保產品順利開發完成並上線,產品經理需要擁有自己的一套原則/產品思維,包括需求分類、用戶體驗、問題導向、數據驅動等,才能讓讓產品團隊更有效地執行項目、降地風險,最終交付解決用戶需求且具有競爭力的產品。
Thumbnail
為了確保產品順利開發完成並上線,產品經理需要擁有自己的一套原則/產品思維,包括需求分類、用戶體驗、問題導向、數據驅動等,才能讓讓產品團隊更有效地執行項目、降地風險,最終交付解決用戶需求且具有競爭力的產品。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News