軟體架構是什麼?又不是什麼?#
本書對**軟體架構(Software Architecture)**的定義如下:
系統的軟體架構是用來推理(reason about)該系統所需的一組結構(structures)。這些結構包含軟體元素、元素之間的關係,以及兩者的屬性。
這個定義刻意避開了其他常見定義中使用的「早期決策」、「重大決策」或「重要決策」等措辭。原因在於:
- 並非所有架構決策都在早期做出——尤其在 Agile 和螺旋式開發中
- 許多早期做出的決策其實並非架構性質
- 「重大」與否往往只有時間才能證明
相比之下,結構在軟體中相對容易辨識,也是系統設計與分析的有力工具。因此:架構的本質是能夠幫助推理的結構。
架構定義的幾個重要含義#
架構是一組軟體結構。 結構是由**關係(relation)所連結的元素(elements)**的集合。一個軟體系統由多個結構組成,沒有任何單一結構能代表整個架構。這些結構可依用途分為三大類別:
- 元件與連接器結構(Component-and-connector structures)
- 模組結構(Module structures)
- 分配結構(Allocation structures)
並非所有結構都是架構性的。舉例來說,「原始碼中包含字母 z 的所有行,按長度遞增排列」也是一個軟體結構,但它既不有趣也不是架構性的。一個結構之所以是架構性的,是因為它能支持對系統及其屬性的推理,而這些屬性對某些利害關係人(stakeholder)是重要的——例如功能性、可用性、可修改性、效能等。架構結構的集合既非固定的也非有限的,什麼是架構性的,取決於在你的情境中對你的系統推理什麼是有用的。
架構是一種抽象。 架構由結構組成,結構由元素和關係組成,因此架構刻意省略某些不利於推理的細節。在所有現代系統中,元素透過介面(interfaces)互動,介面將元素的細節劃分為公開和私有部分。架構關注的是這個劃分的公開面;元素的私有細節——僅與內部實作有關的部分——不是架構性的。這種抽象是馴服系統複雜度的關鍵:我們無法也不想要在所有時間處理所有複雜度。架構的目的正是讓你不必在腦中同時持有一個系統的所有細節。
架構與設計的關係。 架構是設計,但並非所有設計都是架構。許多設計決策由架構留白,交由下游設計者與實作者根據判斷來決定。
每個系統都有架構。 因為每個系統都有元素與關係。但這不代表有人知道它的架構——也許所有設計者都已離開、文件已消失(或從未產出)、原始碼已遺失(或從未交付),手邊只剩下執行中的二進位碼。這揭示了系統架構與架構的表示(representation)之間的差異,也凸顯了架構文件化的重要性(詳見第 22 章)。
並非所有架構都是好的。 本書的定義對架構的好壞保持中立。架構可能有助於或阻礙系統達成重要需求。假設我們不接受以試錯法來選擇架構——隨機挑一個架構、建構系統、然後靠修修補補祈求最好——那麼架構設計(第 20 章)與架構評估(第 21 章)的重要性就不言而喻。
架構包含行為。 每個元素的行為是架構的一部分,只要該行為有助於推理系統。元素的行為體現了它們如何彼此互動、如何與環境互動——這顯然屬於架構定義的範疇,也會影響系統展現的屬性,如執行時期效能。某些行為的面向可能低於架構師的關注層級,但只要元素的行為影響到系統整體的可接受性,就必須被視為架構設計的一部分並加以文件化。
系統架構與企業架構#
兩個相關但範圍更廣的學科:
- 系統架構(System Architecture):涵蓋硬體、軟體與人的整體。軟體架構與系統架構之間常需協商功能分配與限制條件。系統架構允許推理的品質屬性還包括功耗、重量與物理尺寸等。
- 企業架構(Enterprise Architecture):描述組織流程、資訊流、人員與部門的結構與行為。現代企業架構關注軟體系統如何支持業務流程與目標,包括決定企業應支援哪些系統與功能。
這兩者都為軟體架構設定了環境與限制。它們與軟體架構共享許多共通點:都可以被設計、評估、文件化;都回應需求、服務利害關係人;都由結構組成。
架構結構與視圖#
架構結構在自然界和人類活動中都有對應物。就像神經科學家、骨科醫師、血液學家、皮膚科醫師各自看到人體的不同結構,但所有視圖共同描述了人體的架構。建築業也一樣——電工、水管工、暖通空調專家各關注建築物的不同結構。

Figure 1.1: Physiological structures
三大類結構#
1. 元件與連接器結構(Component-and-Connector, C&C)#
C&C 結構關注元素在**執行時期(runtime)**如何互動以執行系統功能。其中:
- 元件(Components):具有執行時期行為的主要運算單元——服務、客戶端、伺服器、過濾器等
- 連接器(Connectors):元件之間的通訊媒介——呼叫-回傳、行程同步、管道等
C&C 結構能回答的問題包括:
- 主要的執行元件是什麼?它們如何在執行時期互動?
- 主要的共享資料儲存區是什麼?
- 系統的哪些部分被複製?
- 資料如何在系統中流動?
- 哪些部分可以平行執行?
- 系統結構能否在執行中改變?
C&C 結構對於推理系統的效能、安全性、可用性等執行時期屬性至關重要,也是最常見的架構結構。

Figure 1.2: A component-and-connector structure
上圖展示了一個 C&C 結構範例:系統包含一個共享儲存庫,由伺服器和管理元件存取;一組客戶端出納員可與帳戶伺服器互動,並透過**發布-訂閱(publish-subscribe)**連接器彼此通訊。
2. 模組結構(Module Structures)#
模組結構將系統切分為實作單元(implementation units),即模組(modules)。模組結構展示系統如何被組織為需要建構或採購的程式碼或資料單元。
- 模組被指派特定的計算職責,是團隊工作分配的基礎
- 模組代表系統的靜態觀點
- 模組的實作形式包括:套件(packages)、類別(classes)、層(layers)
- 模組之間的關係包括:使用(uses)、泛化(generalization / is-a)、組成(is part of)

Figure 1.3: Module elements in UML

Figure 1.4: Module relations in UML
模組結構能回答的問題:
- 每個模組被指派的主要功能職責是什麼?
- 一個模組被允許使用哪些其他軟體元素?
- 它實際使用和依賴哪些軟體?
- 模組之間存在哪些泛化或特化關係?
模組結構也能用於推理當職責改變時對系統的影響,因此是推理系統**可修改性(Modifiability)**的主要工具。
3. 分配結構(Allocation Structures)#
分配結構建立從軟體結構到非軟體結構(組織、開發環境、測試環境、執行環境)的映射。它們能回答:
- 每個軟體元素在哪個處理器上執行?
- 每個元素在開發、測試和系統建置期間儲存在哪些目錄或檔案中?
- 每個軟體元素被分配給哪個開發團隊?
常用的模組結構#
- 分解結構(Decomposition structure):透過「是…的子模組」關係,展示模組如何被遞迴分解為更小的模組。這通常是設計的起點,也很大程度決定了系統的可修改性。常作為開發組織結構、文件結構、整合與測試計畫的基礎。

Figure 1.5: A decomposition structure
- 使用結構(Uses structure):這是一個重要但常被忽視的結構。單元同樣是模組(或類別),以使用(uses)關係連結——這是依賴的一種特化形式。一個軟體單元「使用」另一個,意味著前者的正確性需要後者正確運作的版本存在(而非僅僅是 stub)。使用結構用於工程化可擴展功能的系統,或從中提取有用的功能子集。輕鬆建立系統子集的能力支持了增量式開發(incremental development)。這個結構也是衡量**社會債務(social debt)**的基礎——它定義了哪些團隊應該互相溝通,反映實際通訊量與應有通訊量之間的差距。

Figure 1.6: Uses structure
- 層結構(Layer structure):模組稱為層(layers),每一層是提供一組內聚服務的抽象「虛擬機器」。層之間以受管控的方式使用;在嚴格分層系統中,一層只能使用單一其他層。層結構賦予系統可移植性(portability)。

Figure 1.7: Layer structure
- 類別結構(Class / Generalization structure):以「繼承自」或「是…的實例」關係連結類別。支持對相似行為或能力的推理,以及功能的增量擴展。

Figure 1.8: Generalization structure
- 資料模型(Data model):以資料實體及其關係描述靜態資訊結構。例如銀行系統中的帳戶(Account)、客戶(Customer)、貸款(Loan)等實體及其屬性與關聯。

Figure 1.9: Data model
常用的 C&C 結構#
C&C 結構展示系統的執行時期視圖(runtime view)。在這些結構中,先前描述的模組都已被編譯為可執行形式。因此所有 C&C 結構與模組結構是正交的(orthogonal),處理的是執行中系統的動態面向。例如:一個程式碼單元(模組)可能被編譯成一個服務,然後在執行環境中複製上千次;反之,1,000 個模組也可能被編譯連結成單一的執行時期可執行檔(元件)。
C&C 結構中的關係是附著(attachment),展示元件與連接器如何連結。常用的 C&C 結構包括:
- 服務結構(Service structure):單元是透過訊息等服務協調機制互操作的服務(services)。對於由獨立開發元件組成的系統特別重要。
- 並行結構(Concurrency structure):讓架構師確定平行化機會與資源競爭位置。元件被安排進「邏輯執行緒(logical threads)」——可在後續設計中分配到獨立物理執行緒的計算序列。
常用的分配結構#
分配結構定義 C&C 或模組結構中的元素如何映射到非軟體實體——通常是硬體(可能是虛擬化的)、團隊和檔案系統。
- 部署結構(Deployment structure):展示軟體如何被指派到硬體處理元素和通訊元素。元素包括軟體元素(通常是 C&C 結構中的行程)、硬體實體(處理器)和通訊路徑。關係為「分配到(allocated-to)」——顯示軟體元素位於哪些物理單元上——以及「遷移到(migrates-to)」(若分配是動態的)。可用於推理效能、資料完整性、安全性和可用性,對分散式系統尤其重要,也是實現**可部署性(Deployability)**品質屬性的關鍵結構。

Figure 1.10: Deployment structure
- 實作結構(Implementation structure):展示軟體元素(通常是模組)如何映射到開發、整合、測試或組態管理環境中的檔案結構。
- 工作分配結構(Work assignment structure):將模組的實作與整合職責指派給各個團隊。讓工作分配結構成為架構的一部分,能清楚表明「誰做什麼工作」的決策同時具有架構和管理意涵。架構師因此能了解每個團隊所需的專業能力。例如 Amazon 為每個微服務指派一個專屬團隊,就是一種工作分配結構的體現。在大型開發專案中,識別出功能共通性單元並指派給單一團隊是有益的,而非讓每個需要者各自實作。這個結構也決定了團隊間的主要溝通路徑:定期視訊會議、wiki、郵件列表等。
表 1.1 總結了各結構的元素類型、關係、用途及其影響的品質屬性。例如:分解結構影響可修改性;服務結構影響互操作性、可用性、可修改性;部署結構影響效能、安全性、能源效率、可用性、可部署性。
結構之間的關聯#
各結構提供不同的系統視角,但它們並非獨立——一個結構中的元素會與其他結構中的元素相關。例如,分解結構中的一個模組可能在 C&C 結構中表現為一個、部分一個或多個元件。一般而言,結構之間的映射是多對多的。

Figure 1.11: Two views of a client-server system
上圖以一個簡單的客戶端-伺服器系統為例:左側的模組分解視圖顯示需要實作兩個模組(客戶端軟體和伺服器軟體);右側的 C&C 視圖顯示執行時期有十個客戶端在存取伺服器——因此系統有兩個模組但十一個元件(加上十個連接器)。右側視圖可用於效能分析、瓶頸預測和網路流量管理,這些在左側視圖中幾乎不可能做到。
在 map-reduce 模式中,簡單且相同的功能被分佈到數百或數千個處理節點上——整個系統只有一個模組,但每個節點各有一個元件。這是模組與元件之間非一對一對應的典型範例。
個別專案有時會將一個結構視為主導結構(dominant structure),並盡可能以它來表述其他結構。通常主導結構是模組分解結構,因為它傾向於映射開發團隊結構;在其他專案中,主導結構可能是展示系統功能和關鍵品質屬性如何在執行時期實現的 C&C 結構。
選擇結構的實務建議#
越少越好。 並非所有系統都需要考慮大量架構結構。系統越大,結構之間的差異越顯著;但對於小型系統,通常可以使用較少結構。設計和文件化一個結構,應該只在帶來正面投資報酬時才進行。
選擇哪些結構? 應思考各種可用結構如何對系統最重要的品質屬性提供洞察力和槓桿點,然後選擇最能交付這些屬性的結構。
架構模式#
當架構元素以解決特定問題的方式組合,並在不同領域中反覆被證明有用時,就被記錄並傳播為架構模式(Architectural Patterns)。這些模式提供了打包好的策略來解決系統面臨的問題。本書第二部分將詳細討論架構模式。
什麼是「好的」架構?#
不存在本質上好或壞的架構。 架構只有對某個目的而言更適合或更不適合。例如,一個三層式服務導向架構可能完美適用於大型企業 B2B 網站系統,卻完全不適合航空電子應用;精心設計以實現高可修改性的架構,對一個用完即丟的原型毫無意義(反之亦然!)。
以下的經驗法則可以主動應用於綠地開發(greenfield development)以幫助「做對系統」,也可以作為分析啟發式方法來理解現有系統的潛在問題區域並指引其演化方向。書中將建議分為流程建議和結構建議兩大類。
架構可以(也應該)被評估——這是關注架構的重大好處之一——但評估只有在具體明確的目標背景下才有意義。
流程建議#
- 單一架構師或小型團隊主導。 應有一位明確的技術領導者,以賦予架構**概念完整性(conceptual integrity)**與技術一致性。架構師與開發團隊之間應保持緊密連結,避免「象牙塔」式的不切實際設計。
- 基於優先排序的品質屬性需求。 架構師應持續依據明確規格的品質屬性需求來制定架構,這些需求將指引必然發生的取捨。功能性的重要性較低。
- 使用視圖(views)來文件化。 視圖是一個或多個架構結構的表示。文件應針對最重要利害關係人的關注點,支援專案時程——可以先做最少的文件,之後再擴充。
- 評估架構交付品質屬性的能力。 應在生命週期早期進行評估(此時回報最大),並在適當時重複評估,以確保架構或環境的變化未使設計過時。
- 支持增量實作。 避免一次整合所有東西。可透過建立「骨架系統(skeletal system)」來實現——先建立通訊路徑但只有最小功能,然後漸進式地「生長」系統。
結構建議#
- 定義良好的模組。 根據**資訊隱藏(information hiding)和關注點分離(separation of concerns)**原則分配功能職責。資訊隱藏模組應封裝可能變化的部分,從而隔離軟體免受這些變化的影響。每個模組都應有明確定義的介面,封裝或「隱藏」可變面向,讓各開發團隊能大致獨立工作。
- 使用已知的架構模式與策略。 除非需求前所未有(可能但不太可能),否則品質屬性應透過針對各屬性的已知架構模式和策略來實現(詳見第 4 至 13 章)。
- 不依賴特定版本的商業產品或工具。 若不得不依賴,結構應使得更換到不同版本既直觀又低成本。
- 資料生產者與消費者分離。 這傾向於提高可修改性,因為變更通常被限制在資料的生產端或消費端。當新增資料時,兩端都需要改變,但分離允許分階段(incremental)升級。
- 不要期望模組與元件一對一對應。 在具有並行性的系統中,同一模組可能有多個元件實例平行執行。在多執行緒並行的系統中,每個執行緒可能使用多個元件的服務,而每個元件各自由不同模組建構。
- 行程應可輕易重新指派處理器。 甚至在執行時期也能改變。這是虛擬化和雲端部署日益增長趨勢的驅動力(詳見第 16、17 章)。
- 少量且簡單的元件互動模式。 系統應在各處以相同方式做相同的事。這種做法有助於理解性、降低開發時間、提高可靠性和可修改性。
- 明確且少量的資源競爭區域。 其解決方案應被清楚規格化和維護。例如,若網路使用率是關注點,架構師應為每個開發團隊制定並強制執行網路流量指南;若效能是關注點,架構師應制定並強制執行時間預算(time budgets)。
未遵循任何一條指南並不自動意味著架構有致命缺陷,但至少應視為值得調查的警示訊號。
本章摘要#
軟體架構是推理系統所需的一組結構,包含軟體元素、元素間的關係及兩者的屬性。三大類結構各有其功能:
- 模組結構:展示需要建構或採購的程式碼/資料單元
- C&C 結構:展示具有執行時期行為的元素及其互動
- 分配結構:展示模組和 C&C 結構的元素如何對應到非軟體結構
結構代表架構的主要工程槓桿點,每個結構都帶有操控一個或多個品質屬性的能力。每個系統都有軟體架構,但架構可能未被文件化和傳播。不存在本質上好或壞的架構——架構只有對某個目的而言更適合或更不適合。