概述#

介面(Interface)是架構元素之間互動的基本抽象機制,對系統的 modifiabilityusabilitytestabilityperformanceintegrability 等品質屬性有著深遠影響。本章聚焦於介面的概念、設計原則與文件化方法。

書中開宗明義指出三個關鍵前提:

  • 所有元素都有介面 — 否則該元素沒有存在的意義
  • 介面是雙向的 — 不只描述元素「提供」什麼,也描述元素「需要」什麼
  • 同一介面可服務多個 Actor — 例如 Web Server 同時處理多個 HTTP 連線

介面概念(Interface Concepts)#

多重介面(Multiple Interfaces)#

一個元素可以將單一介面拆分為多個介面,每個介面服務不同類別的 Actor,提供關注點分離(Separation of Concerns)。例如:

  • 主要介面暴露核心功能
  • 除錯或效能監控介面供維運人員使用
  • 公開唯讀介面供匿名使用者,私有介面供認證與授權的使用者

資源(Resources)#

介面資源具有兩個面向:

  • 語法(Syntax):資源的簽章(Signature),包括名稱、參數的名稱與資料型別等
  • 語意(Semantics):呼叫資源後的效果,包括:
    • 賦值給呼叫者可存取的資料
    • 跨越介面的值的假設
    • 元素狀態的改變(含異常與副作用)
    • 因使用資源而觸發的事件或訊息
    • 對其他資源未來行為的影響
    • 人類可觀察的結果(常見於嵌入式系統)

操作、事件與屬性(Operations, Events, and Properties)#

介面提供的資源由三種類型組成:

  • 操作(Operations):將控制與資料轉移給元素進行處理,通常會回傳結果,失敗時需明確的錯誤偵測機制
  • 事件(Events):通常為非同步的;傳入事件代表從佇列接收訊息或串流元素,主動元素產生傳出事件以通知訂閱者
  • 屬性(Properties):介面的 metadata,如存取權限、度量單位或格式假設,屬性值會影響操作的行為

具有狀態且為主動型的複雜元素,其介面通常會同時包含操作、事件和屬性的組合。

介面演化(Interface Evolution)#

介面也需要演化,但因為介面是元素與 Actor 之間的契約,變更必須謹慎。三種演化技術:

  • 棄用(Deprecation):移除介面前應充分通知 Actor,最佳做法是引入錯誤碼告知棄用時程
  • 版本控制(Versioning):保留舊介面、新增新介面,Actor 需指定使用的版本
  • 擴展(Extension):保留原始介面不變,新增資源。若擴展不相容,則需加入**中介者(Mediator)**進行轉譯

Figure 15.1: Interface evolution: original, extending, and using an intermediary

當介面擴展引入不相容的變更時,可透過中介者模式在外部介面與內部介面之間進行轉譯,避免破壞既有的 Actor。

設計介面(Designing an Interface)#

設計原則#

將資源加入介面意味著承諾長期維護該資源。以下是幾項重要的介面設計原則:

  • 最小驚訝原則(Principle of Least Surprise):介面行為應符合 Actor 的預期,命名尤其重要
  • 小介面原則(Small Interfaces Principle):兩個元素互動時,交換的資訊應盡可能少
  • 統一存取原則(Uniform Access Principle):避免透過介面洩漏實作細節,Actor 不應知道值是來自快取、計算還是外部取得
  • 不重複原則(Don’t Repeat Yourself):介面應提供可組合的基本操作,而非多種冗餘方式達成相同目標

一致性是設計清晰介面的關鍵。架構師應建立並遵循資源命名、API 參數排序和錯誤處理的慣例,以減少因誤解而產生的開發錯誤。

成功的介面互動需要在以下四個面向達成共識:介面範圍互動風格交換資料的表示與結構錯誤處理

介面範圍(Interface Scope)#

介面範圍定義了 Actor 可直接存取的資源集合。常見的限制與中介存取模式是建立 Gateway 元素

  • Gateway 將 Actor 的請求轉譯為目標元素的資源請求
  • 當元素提供的資源粒度與 Actor 需求不同時,Gateway 可進行轉譯
  • Actor 可能需要存取特定子集的資源
  • 資源的數量、協定、類型、位置和屬性可能隨時間改變,Gateway 提供更穩定的介面

Figure 15.2: A gateway that provides access to different resources

互動風格(Interaction Styles)#

介面的目的是連接不同元素以進行資料傳輸(通訊)和控制轉移(協調)。互動風格取決於元素是否共置或遠端部署:

  • 共置元素可透過本地共享記憶體緩衝區高效存取大量資料
  • 同時可用的元素可使用同步呼叫
  • 不可靠分散式環境中的元素需依賴非同步互動,透過訊息佇列或資料串流交換事件

書中聚焦兩種最廣泛使用的互動風格:

Remote Procedure Call (RPC)

  • 模仿命令式語言的程序呼叫,但被呼叫的程序位於網路的另一端
  • 最新版本 gRPC 支援二進位參數傳輸、非同步、認證、雙向串流、流量控制、阻塞/非阻塞綁定、取消與逾時
  • gRPC 使用 HTTP 2.0 作為傳輸層

Representational State Transfer (REST)

  • Web 服務的協定,由六個約束組成:
    • 統一介面(Uniform Interface):所有互動使用相同形式(通常為 HTTP),資源透過 URI 指定
    • 客戶端-伺服器(Client-Server):使用 client-server 模式
    • 無狀態(Stateless):所有互動無狀態,授權等資訊編碼為 token 隨每次請求傳送
    • 可快取(Cacheable):資源可在伺服器端或客戶端快取
    • 分層系統架構(Tiered System Architecture):伺服器可拆分為多個獨立部署的元素
    • 按需程式碼(Code on Demand):伺服器可選擇性地提供程式碼給客戶端執行(如 JavaScript)

HTTP 最常搭配 REST 使用,其基本命令與 CRUD 操作的對應關係如下:

HTTP 命令CRUD 操作
POSTCreate
GETRead
PUTUpdate/Replace
PATCHUpdate/Modify
DELETEDelete

交換資料的表示與結構#

介面可將內部資料表示抽象為更適合跨語言交換和網路傳輸的外部表示。從內部轉換為外部表示稱為序列化(Serialization)。選擇資料交換格式需考量:

  • 表達力(Expressiveness):能否序列化任意資料結構
  • 互通性(Interoperability):表示是否符合 Actor 的期望與解析能力
  • 效能(Performance):頻寬使用效率與解析的演算法複雜度
  • 隱含耦合(Implicit Coupling):Actor 與元素之間可能導致錯誤的共享假設
  • 透明度(Transparency):訊息是否容易被攔截與觀察(自描述訊息便於除錯但安全性較低)

三種主要的資料表示方式:

XML

  • W3C 於 1998 年標準化的元語言(meta-language)
  • 使用 tag 標註文件,可透過 XML Schema 定義自訂語言
  • 支援驗證文件是否符合 schema,但解析成本高、冗長

JSON

  • 使用巢狀的 name/value pair 和陣列資料型別
  • 源自 JavaScript,2013 年首次標準化
  • 比 XML 更簡潔,序列化/反序列化更高效
  • 文件可在讀取時即時解析

Protocol Buffers

  • Google 開發的二進位格式
  • 極為緊湊,記憶體和網路頻寬使用效率高
  • 主要搭配 gRPC 使用
  • 透過 .proto 檔案定義 schema,由語言特定的編譯器生成程式碼
  • 介面規格可作為文件並存入資料庫供搜尋

文字格式(XML、JSON)便於除錯但較佔頻寬;二進位格式(Protocol Buffers)緊湊高效但需特殊工具除錯。選擇時應根據系統的效能需求與開發體驗做權衡。

錯誤處理(Error Handling)#

良好的介面設計必須考量非正常情境。Actor 偵測錯誤的策略包括:

  • 失敗的操作拋出例外(Exception)
  • 操作回傳預定義的狀態碼(Status Code)
  • 使用屬性儲存最新操作是否成功的資訊
  • 觸發錯誤事件(如逾時)
  • 透過特定輸出資料串流讀取錯誤日誌

常見的錯誤來源:

  • 傳送了不正確、無效或非法的資訊
  • 元素處於錯誤狀態(可能因先前操作或缺少先前操作)
  • 硬體或軟體錯誤阻礙了元素的成功執行
  • 元素未正確配置(例如資料庫連線字串指向錯誤的伺服器)

指明錯誤來源有助於系統選擇適當的修正策略:暫時性錯誤可重試、輸入無效需修正請求、缺少相依性需重新安裝、實作 bug 應加入測試案例以避免回歸。

文件化介面(Documenting the Interface)#

介面文件記錄了其他開發者使用該介面所需知道的資訊。撰寫時需注意:

  • 只揭露 Actor 需要知道的資訊,而非所有可觀察的行為
  • 不同利害關係人需要不同層級的資訊

主要利害關係人角色:

  • 元素開發者:需知道介面必須履行的契約
  • 維護者:在最小化對既有 Actor 干擾的情況下修改元素與介面
  • 使用介面的開發者:需理解介面契約與使用方式
  • 系統整合者與測試者:需要所有資源與功能的詳細資訊
  • 分析師:例如效能分析師需要 SLA 保證
  • 尋求重用資產的架構師:關注介面資源的能力、品質屬性與可變性

Hyrum’s Law 指出:「當介面的使用者足夠多時,你在契約中承諾什麼已不重要 – 系統所有可觀察的行為都會被某些人所依賴。」因此,依賴未公開行為的 Actor 需自行承擔風險。

總結#

  • 架構元素透過介面互動,介面設計是架構師的核心職責
  • 元素可擁有多重介面,為不同類別的 Actor 提供不同層級的存取
  • 介面由操作、事件和屬性組成
  • 設計介面需決定範圍、互動風格、資料表示/結構/語意、錯誤處理
  • 資料交換可使用 XML、JSON 或 Protocol Buffers 等標準化機制
  • 介面演化的三種技術:棄用、版本控制、擴展
  • 介面文件應詳述語法與語意,並針對不同利害關係人提供適當層級的資訊