本章為全書奠定基調:軟體架構沒有「最佳實務」,只有在特定情境下的 最不差的取捨組合(least worst combination of trade-offs)。作者引用 Fred Brooks 的名言強調,技術領域不存在銀彈,架構師的真正工作是客觀地評估每個決策兩側的取捨,並做出最好的判斷。

不要試圖找到軟體架構中的「最佳」設計,而是要追求 最不差的取捨組合。這本書的核心就是關於如何在面對全新情境時做出更好的決策。

為什麼叫「The Hard Parts」?#

書名中的 “hard” 有雙重含義:

  • 困難(difficult):架構師經常面對前所未有的難題,涉及大量技術決策,加上人際與政治環境的層層考量
  • 堅硬(solid):如同 hardware 與 software 的區分,architecture 是結構性的基礎,不應經常變動;而 design 則相對容易改變。本書聚焦於架構中這些 基礎性的、不易改變的部分

一個實用的定義:「軟體架構就是那些之後很難改變的東西(stuff)。」

給予歷久彌新的建議#

  • 軟體開發生態系不斷演變——十年前的主流是 orchestration-driven、SOA 架構,如今則是 microservices
  • 架構風格的演變受到 技術約束環境條件 的驅動(例如 open source 與 Linux 的普及促成了微服務革命)
  • 本書 不聚焦於特定技術或實作細節,而是聚焦於架構師 如何做決策、如何客觀地權衡取捨
  • 使用當代的場景與範例提供背景,但底層原則著重於 取捨分析與決策方法論

資料在架構中的重要性#

Tim Berners-Lee:「資料是珍貴的,它的壽命比系統本身更長。」

  • 資料是企業最重要的資產:每個系統都必須處理資料,而資料往往比系統或架構存活更久
  • 資料架構師建構緊密耦合系統的慣性,與現代分散式架構產生衝突
  • 在微服務與 Bounded Context(Domain-Driven Design)的理念下,資料已從單純的實作細節提升為 架構層級的關注點
  • 現代架構的許多困難之處,源自 資料與架構之間的張力

操作型資料 vs. 分析型資料#

  • Operational data(操作型資料):用於業務運作的資料,如銷售、庫存、交易資料。屬於 OLTP(Online Transactional Processing),涉及新增、修改、刪除操作。若中斷,組織無法正常運作
  • Analytical data(分析型資料):用於預測、趨勢分析、商業智慧。通常非交易性質,可能儲存在 graph database 或快照中。非日常營運所需,而是用於 長期策略決策

Architectural Decision Records (ADR)#

ADR 是記錄架構決策最有效的方式之一,由 Michael Nygard 首先推廣,後被 Thoughtworks Technology Radar 標記為「adopt」。

  • ADR 是一份簡短的文字檔案(通常一到兩頁),描述一個特定的架構決策
  • 可用純文字、AsciiDoc、Markdown 或 wiki 頁面撰寫

ADR 格式#

本書中每個架構決策都使用以下格式記錄:

  • ADR 標題:包含架構決策的簡短名詞片語
  • Context(背景):簡短描述問題,並列出替代方案
  • Decision(決策):陳述架構決策並提供詳細的理由
  • Consequences(後果):描述決策後的影響,以及所考量的取捨

Architecture Fitness Functions#

什麼是 Fitness Function?#

當架構師定義好元件之間的關係並編入設計之後,如何確保實作者會遵守?這屬於 architecture governance(架構治理) 的範疇。

Architecture fitness function 的定義:任何對某個架構特性或架構特性組合執行客觀完整性評估的機制。

定義拆解:

  • 任何機制(Any mechanism):可使用各種工具實現,包含測試框架、監控工具、chaos engineering 框架等
  • 客觀完整性評估(Objective integrity assessment):必須提供可由測試、監控或 fitness function 衡量的客觀值
  • 架構特性(Architecture characteristic):注意 composite 特性(如 agility)本身不可測量,需拆解為可測量的子特性(如 deployability、testability、cycle time)

Fitness Function 的範疇#

  • Atomic:處理單一架構特性,例如檢測 codebase 中的元件循環依賴
  • Holistic:驗證多個架構特性的組合效果,例如安全性改善可能影響效能,scalability 與 elasticity 有時互相矛盾

實際範例#

Figure 1.1: Cyclic dependencies between components

  • 使用 JDepend 撰寫 fitness function 偵測 Java 套件間的循環依賴,整合到 CI pipeline 中
  • 使用 ArchUnit 驗證分層架構的層級存取規則(如 Controller 不被任何層存取、Service 只能被 Controller 存取)
  • 使用 NetArchTest 在 .NET 平台做類似的層級依賴驗證

Figure 1.2: Traditional layered architecture

Fitness Function 的特性#

  • Static vs. Dynamic:有些回傳固定值(如 true/false),有些根據情境回傳動態值(如效能指標隨並發用戶數變化)
  • Automated vs. Manual:大多應自動化並持續執行;但某些情境(如法律合規審查)仍需手動
  • 應整合到 CI/CD pipeline 中持續運行

Fitness function 本質上是架構師建立的 可執行的架構規格——是關於「重要但不緊急」原則的自動化檢查清單,防止開發人員有意或無意地違反架構原則。

企業級治理範例#

  • 當發現 zero-day 漏洞時,安全團隊可在每個專案的 deployment pipeline 中插入檢查特定框架版本的測試
  • 若發現危險版本,構建失敗並通知安全團隊
  • 這種機制允許企業 普遍地自動化重要的治理任務

Architecture vs. Design:保持定義簡單#

架構師經常掙扎於區分 architecturedesign。本書聚焦於架構端,原因有三:

  1. 架構師必須理解底層架構原則才能做有效決策(例如同步 vs. 非同步通訊的取捨在實作之前就需要考量)
  2. 聚焦架構概念可避免陷入各種實作方式的細節
  3. 聚焦架構原則讓內容盡可能保持通用性

本書使用的關鍵定義#

  • Service:一組內聚的功能,部署為獨立的可執行單元
  • Coupling:兩個 artifact 之間的關聯——一方的改變可能需要另一方也跟著改變
  • Component:應用程式中的架構構建塊,通常透過 package structure、namespace 或目錄結構體現
  • Synchronous communication:呼叫方必須等待回應才能繼續
  • Asynchronous communication:呼叫方不需等待回應即可繼續
  • Orchestrated coordination:工作流中有一個專責服務負責協調
  • Choreographed coordination:工作流中沒有協調者,各服務共同分擔協調責任
  • Atomicity:工作流的所有部分在任何時間都維持一致的狀態
  • Contract:兩個軟體部件之間的介面定義,涵蓋方法呼叫、整合架構、遠端呼叫、依賴等

引入 Sysops Squad Saga#

為了將抽象的架構概念具體化,本書引入了一個貫穿全書的案例故事:Sysops Squad

背景#

  • Penultimate Electronics 是一家大型電子產品零售商,客戶可購買支援方案
  • 當產品出問題時,技術專家(Sysops Squad)會到客戶家中維修

四種使用者角色#

  • Administrator:維護內部使用者、專家清單、帳務處理、靜態參考資料
  • Customer:註冊服務、維護個人資料、提交問題工單、填寫服務後問卷
  • Sysops Squad Expert:被分派問題工單並修復問題,使用知識庫搜尋解決方案
  • Manager:追蹤問題工單作業狀況,接收營運與分析報告

Nonticketing Workflow(非工單流程)#

  1. 管理員新增和維護 Sysops Squad 專家資料(地區、可用性、技能)
  2. 客戶註冊系統,擁有多個基於產品的支援方案
  3. 客戶依信用卡資訊按月自動扣款,可查看帳單記錄
  4. 管理者請求和接收各類營運與分析報告

Ticketing Workflow(工單流程)#

  1. 客戶透過網站提交問題工單
  2. 系統根據技能、地區、可用性自動分派最適合的專家
  3. 工單發送至專家的行動裝置 App,並以簡訊通知
  4. 透過 SMS 或 email 通知客戶專家即將到達
  5. 專家使用行動 App 查看工單資訊與知識庫
  6. 專家修復問題後標記工單完成,並更新知識庫
  7. 系統發送問卷連結給客戶
  8. 系統記錄客戶填寫的問卷結果

A Bad Scenario(困境)#

目前的問題工單系統是多年前開發的 大型單體應用(monolith),面臨嚴重問題:

  • 客戶抱怨顧問不出現、工單遺失、派錯專家
  • 系統不穩定,經常「凍結」或當機,導致所有功能中斷數分鐘到數小時
  • 每次變更都很困難且高風險,改一個地方往往會破壞另一個地方
  • 若不盡快解決,Penultimate Electronics 將被迫放棄支援合約業務

Sysops Squad 架構元件#

現有的單體應用包含以下元件(皆在 ss. namespace 下):

元件Namespace職責
Loginss.login內部與客戶登入及安全邏輯
Billing paymentss.billing.payment客戶月費帳務與信用卡資訊
Billing historyss.billing.history付款記錄與帳單
Customer notificationss.customer.notification通知客戶帳務與一般資訊
Customer profiless.customer.profile客戶資料維護與註冊
Expert profiless.expert.profile專家資料(姓名、地區、技能)
KB maintss.kb.maintenance知識庫維護與查看
KB searchss.kb.search知識庫搜尋引擎
Reportingss.reporting所有報表(專家、工單、財務)
Ticketss.ticket工單建立、維護、完成
Ticket assignss.ticket.assign尋找專家並分派工單
Ticket notifyss.ticket.notify通知客戶專家即將到達
Ticket routess.ticket.route將工單發送至專家行動 App
Support contractss.supportcontract客戶支援合約與產品
Surveyss.survey問卷管理與結果記錄
Survey notifyss.survey.notify發送問卷 email 給客戶
Survey templatesss.survey.templates依服務類型維護問卷範本
User maintenancess.users內部使用者與角色維護

Figure 1.3: Components within the existing Sysops Squad application

Sysops Squad 資料模型#

  • 所有元件共用 單一資料庫 schema,採用標準的第三正規化資料模型
  • 資料庫儲存客戶、使用者、合約、帳務、付款、知識庫、問卷等資料
  • 包含少量 stored procedure 與 trigger,但有大量由 Reporting 元件使用的 view
  • 當架構團隊嘗試拆分單體、轉向分散式架構時,必須與資料庫團隊合作來拆分資料庫——這正是本書後續章節要探討的各種技術與取捨

Figure 1.4: Data model within the existing Sysops Squad application