本章為全書奠定基調:軟體架構沒有「最佳實務」,只有在特定情境下的 最不差的取捨組合(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:保持定義簡單#
架構師經常掙扎於區分 architecture 與 design。本書聚焦於架構端,原因有三:
- 架構師必須理解底層架構原則才能做有效決策(例如同步 vs. 非同步通訊的取捨在實作之前就需要考量)
- 聚焦架構概念可避免陷入各種實作方式的細節
- 聚焦架構原則讓內容盡可能保持通用性
本書使用的關鍵定義#
- 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(非工單流程)#
- 管理員新增和維護 Sysops Squad 專家資料(地區、可用性、技能)
- 客戶註冊系統,擁有多個基於產品的支援方案
- 客戶依信用卡資訊按月自動扣款,可查看帳單記錄
- 管理者請求和接收各類營運與分析報告
Ticketing Workflow(工單流程)#
- 客戶透過網站提交問題工單
- 系統根據技能、地區、可用性自動分派最適合的專家
- 工單發送至專家的行動裝置 App,並以簡訊通知
- 透過 SMS 或 email 通知客戶專家即將到達
- 專家使用行動 App 查看工單資訊與知識庫
- 專家修復問題後標記工單完成,並更新知識庫
- 系統發送問卷連結給客戶
- 系統記錄客戶填寫的問卷結果
A Bad Scenario(困境)#
目前的問題工單系統是多年前開發的 大型單體應用(monolith),面臨嚴重問題:
- 客戶抱怨顧問不出現、工單遺失、派錯專家
- 系統不穩定,經常「凍結」或當機,導致所有功能中斷數分鐘到數小時
- 每次變更都很困難且高風險,改一個地方往往會破壞另一個地方
- 若不盡快解決,Penultimate Electronics 將被迫放棄支援合約業務
Sysops Squad 架構元件#
現有的單體應用包含以下元件(皆在 ss. namespace 下):
| 元件 | Namespace | 職責 |
|---|---|---|
| Login | ss.login | 內部與客戶登入及安全邏輯 |
| Billing payment | ss.billing.payment | 客戶月費帳務與信用卡資訊 |
| Billing history | ss.billing.history | 付款記錄與帳單 |
| Customer notification | ss.customer.notification | 通知客戶帳務與一般資訊 |
| Customer profile | ss.customer.profile | 客戶資料維護與註冊 |
| Expert profile | ss.expert.profile | 專家資料(姓名、地區、技能) |
| KB maint | ss.kb.maintenance | 知識庫維護與查看 |
| KB search | ss.kb.search | 知識庫搜尋引擎 |
| Reporting | ss.reporting | 所有報表(專家、工單、財務) |
| Ticket | ss.ticket | 工單建立、維護、完成 |
| Ticket assign | ss.ticket.assign | 尋找專家並分派工單 |
| Ticket notify | ss.ticket.notify | 通知客戶專家即將到達 |
| Ticket route | ss.ticket.route | 將工單發送至專家行動 App |
| Support contract | ss.supportcontract | 客戶支援合約與產品 |
| Survey | ss.survey | 問卷管理與結果記錄 |
| Survey notify | ss.survey.notify | 發送問卷 email 給客戶 |
| Survey templates | ss.survey.templates | 依服務類型維護問卷範本 |
| User maintenance | ss.users | 內部使用者與角色維護 |

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