破除迷思:釐清微服務中ER圖的虛實之別

當組織從單體架構轉向微服務時,資料模型的設計方式經常成為爭議的焦點。數十年來,實體-關係圖(ERD)一直是集中式系統資料庫設計的藍圖。它精確地繪製了資料表、欄位、金鑰與關係。然而,微服務的分散特性挑戰了這些傳統規範。認為整個系統都適用於單一統一的資料結構,是一種根深蒂固的誤解,可能導致緊密耦合與運營上的脆弱性。

本指南探討了分散環境中關於ER圖的常見觀點。它區分事實與虛構,專注於資料邊界應如何定義、在無共享資料表的情況下如何管理關係,以及為何在轉向服務導向架構時,資料的視覺呈現必須改變。目標是提供對支援可擴展性與韌性的資料模型原則的清晰理解。

Hand-drawn whiteboard infographic comparing monolithic versus microservices data architecture, illustrating three busted myths about ER diagrams in distributed systems: the one-database fallacy, strong consistency requirements, and ERD obsolescence; shows best practices including database-per-service pattern, domain-driven design, eventual consistency, API composition, and local ERDs for bounded contexts with color-coded markers for concepts, warnings, and solutions

單體遺產:為何舊的ER圖不再適用 🏛️

在傳統的單體應用中,資料庫扮演著唯一真實來源的角色。所有應用邏輯都與單一資料結構互動。這種環境有利於建立完整涵蓋每個實體與關係的ER圖。設計師可以依賴外鍵來確保整個系統的參考完整性。交易會跨越同一資料庫實例中的多個資料表,確保ACID特性(原子性、一致性、隔離性、耐久性)在全球範圍內維持。

當這種思維應用於微服務時,摩擦便會產生。微服務被設計為自主的。每個服務都管理自己的資料持久層。這意味著服務之間並無共享資料庫。若一個服務擁有其資料,另一個服務便無法使用標準SQL連接直接查詢。因此,ER圖必須從全域性的地圖轉變為一系列領域特定的資料結構。

  • 集中式控制: 單體架構允許資料庫管理員(DBA)管理整個資料結構。
  • 分散式所有權: 微服務要求每個團隊負責其資料結構的定義。
  • 全域交易: 單體架構支援跨資料表的單一交易更新。
  • 分散式交易: 微服務需要使用Sagas或最終一致性等協調模式。

現代化資料模型的第一步,是接受單一涵蓋整個應用程式的ER圖已不再可行或理想。相反地,重點轉向領域驅動設計,使資料模型與每個服務的業務能力保持一致。

迷思一:「單一資料庫」的謬誤 🗄️❌

許多剛接觸分散式系統的架構師常有一種信念,認為即使在邏輯上使用資料庫前綴或獨立資料表來分離資料,仍可維持單一的實體資料庫。這種做法常被稱為「共享資料庫」的反模式。雖然看似簡化了初始設計,但隨著系統擴展,會帶來顯著風險。

為何共享資料庫會失敗

即使服務之間不共享程式碼,共享資料庫實例仍會造成實體上的耦合。若某一服務需要進行影響效能或可用性的資料結構遷移,所有共用該資料庫的其他服務都會受到影響。這違反了微服務中獨立性的核心原則。

  • 部署阻塞: Service A 的風險遷移可能導致 Service B 無法部署。
  • 資源競爭: 某一服務的大量查詢可能導致其他服務的效能下降。
  • 安全風險: 一個被入侵的服務可能竊取另一個服務的資料。
  • 技術綁定: 若 Service A 需要與 Service B 不同的資料庫引擎,則無法在共享環境中共存。

解決方案是採用「每個服務對應一個資料庫」的模式。每個服務都自行配置自己的資料庫。這確保了資料結構的變更彼此隔離。Service A 的ER圖應僅反映Service A所需的資料實體,而非整個系統的全域結構。

迷思二:強一致性永遠都是必要的 ⚖️

在單體環境中,ACID合規性是標準。開發人員預期一旦交易提交,資料就會在整個系統中立即保持一致。但在微服務中,這種期望往往不切實際。CAP定理指出,分散式系統只能確保三項特性中的兩項:一致性、可用性與分割容忍度。

理解分散式一致性

當服務透過網路進行通訊時,延遲與潛在的失敗是不可避免的。試圖在服務邊界之間強制執行強一致性,通常會導致高延遲或系統無法使用。相反地,許多系統採用最終一致性。這表示服務之間的資料可能會暫時不一致,但會隨著時間逐漸趨於一致。

  • 強一致性: 資料會立即在所有地方更新。適合銀行系統,但會導致高延遲。
  • 最終一致性: 資料以非同步方式傳播。適合使用者資料、庫存數量等情境。
  • 基本可用性: 即使在網路分割的情況下,系統仍能保持運作。

微服務中的ER圖通常不會表示需要立即鎖定的關係。相反地,它代表的是本地一致的資料狀態。跨服務的關係是透過事件或API呼叫來處理,而非資料庫的外鍵。

迷思3:ER圖在分散式系統中已過時 📉

一些實務工作者認為,由於微服務將資料解耦,ER圖的概念已不再必要。這是錯誤的觀點。雖然全域ER圖已過時,但本地ER圖的重要性反而比以往更高。若未清楚記錄服務內部的資料結構,資料偏移與整合錯誤的風險將顯著增加。

本地ER圖的角色

在微服務架構中,ER圖所扮演的角色與單體系統不同。它定義了邊界上下文,確保服務明確知道自身擁有哪些資料,以及這些資料在內部的結構。它無需顯示與外部服務的關係。

  • 文件化: 它作為內部資料模型的合約。
  • 溝通: 它幫助開發人員理解領域實體,而無需了解外部依賴。
  • 維護: 它簡化了新成員加入特定服務的入門流程。
  • 驗證: 它有助於在設計階段識別循環依賴。

該圖表應專注於實體、屬性與主要鍵。參考外部服務的外鍵應予以移除,或抽象為識別符,而非直接的資料表連結。

微服務資料模型設計的最佳實務 🛠️

為了建構穩健的系統,團隊必須採用符合分散式架構原則的特定模型策略。這些實務確保服務保持獨立,同時仍能協作以提供一致的使用者體驗。

1. 領域驅動設計(DDD)

將資料庫結構與領域模型對齊至關重要。每個服務應代表特定的業務能力。例如,「使用者服務」不應儲存訂單細節,「訂單服務」也不應儲存使用者驗證憑證。這種分離確保ER圖反映的是業務邏輯,而非技術上的便利性。

  • 根據交易邊界定義聚合。
  • 讓ER圖專注於服務的責任範圍。
  • 避免建立橫跨多個業務領域的模型。

2. 處理邊界之間的關係

當服務 A 需要服務 B 擁有的資料時,不應直接查詢服務 B 的資料庫。相反,應使用以下其中一種模式:

  • API 組合: 服務 A 呼叫服務 B 的 API 以取得必要的資料。
  • 最終複製: 服務 A 在其自身的資料庫中維護必要資料的副本,並透過事件進行更新。
  • 透過讀取模型進行合併: 專用的讀取服務會從多個來源聚合資料,以優化查詢。

3. 資料結構版本控制

在分散式系統中,服務以不同的速度演進。一個服務的資料結構變更不應破壞該服務的使用者。實施資料結構版本控制可確保向後相容性。

  • 為 API 合約使用版本化的端點。
  • 在遷移期間允許多個資料結構版本共存。
  • 應逐步淘汰舊的資料結構版本,而非強制立即更新。

對比:單體架構 vs. 微服務資料架構 📊

為釐清差異,下表概述了集中式與分散式架構中資料模型設計的主要區別。

功能 單體架構 微服務架構
資料儲存 單一資料庫實例 每個服務對應一個資料庫
ER 圖的範圍 全域系統視圖 服務特定視圖
關係 外鍵(SQL 連接) API 呼叫或事件
一致性模型 強一致性(ACID) 最終一致性(BASE)
部署 單體部署 獨立服務部署
結構變更 集中式遷移 由服務團隊擁有
查詢 直接 SQL 讀取模型 / CQRS

處理跨邊界之資料關係 🔗

微服務中最困難的方面之一就是管理資料關係。在單體系統中,外鍵可確保訂單屬於某位使用者。在微服務中,「使用者」資料表位於使用者服務中,而「訂單」資料表則位於訂單服務中。訂單服務無法持有指向使用者服務資料庫的外鍵。

參考完整性模式

為了在不共用資料表的情況下維持參考完整性,團隊可以使用特定模式:

  • 邏輯引用: 將使用者 ID 儲存為字串或數字,但在建立時透過 API 呼叫驗證其存在性。
  • 資料庫觸發器: 不建議跨服務使用,但在單一服務內是有效的。
  • 驗證事件: 使用者服務發佈「使用者已建立」事件,訂單服務則消費此事件以確認關係存在。

連接的問題

跨服務邊界的連接會造成效能瓶頸,會引入網路延遲與潛在的失敗點。如果使用者服務當機,訂單服務若依賴連接將無法取得訂單細節。因此,訂單服務應在建立訂單時,冗餘儲存必要的使用者資訊(如姓名)。這是在資料正規化與可用性之間的權衡。

結構演進與版本控制 🔄

結構演進是不可避免的。隨著業務需求變動,資料結構必須適應調整。在微服務環境中,變更結構會更為複雜,因為多個服務可能依賴另一個服務的資料結構。

演進策略

  • 新增變更: 若應用程式能妥善處理遺失欄位的情況,新增欄位通常是安全的。
  • 欄位移除: 這需要一段棄用期間,期間欄位雖被隱藏但仍存在,之後再予以移除。
  • 類型變更: 變更資料類型(例如從字串變為整數)需要協調一致的遷移策略。

使用結構註冊中心可協助管理這些變更。它作為服務間交換資料結構的中央可信來源,確保生產者與消費者對格式達成一致。

應避免的常見陷阱 🚧

即使對原則有穩固的理解,團隊在實施過程中仍經常陷入陷阱。及早識別這些陷阱可大幅減少技術債務。

  • 過度規範化:試圖在所有服務之間維持單一真相來源,會導致複雜的分散式交易。必要時應接受冗餘。
  • 忽略冪等性:網路呼叫可能失敗或重試。資料操作必須設計成能處理重複請求,而不會產生重複資料。
  • 編排過載:僅依賴事件來維持資料一致性可能變得難以管理。對於複雜的工作流程,應使用編排。
  • 低估延遲:跨服務資料擷取會為每次請求增加毫秒級延遲。盡可能在本地聚合資料。
  • 缺乏文件:若每個服務缺乏明確的實體關係圖,整合便成了猜謎遊戲。

關於架構清晰度的最後想法 🧠

從單體架構轉向微服務資料模型,需要思維上的轉變。這不僅僅是將資料庫拆分成更小的單元,更在於重新定義資料所有權與關係的構思方式。實體關係圖仍是關鍵工具,但其範圍應縮小至服務邊界內。

透過避免共享資料庫與全域一致性等迷思,架構師可建構出具彈性與可擴展性的系統。關鍵在於優先考量服務自主性,而非資料規範化。這意味著必須接受部分資料會被複製,以確保服務能獨立運作。也意味著要理解強一致性僅是奢華,並非每項操作的必要條件。

設計資料架構時,應聚焦於領域。讓業務能力決定邊界。使用實體關係圖釐清每個服務的內部狀態,並透過事件與API來管理彼此之間的連接。此方法可確保系統能持續演進,而不破壞底層的資料完整性。

最終目標並非以分散形式重複單體架構。而是建立一個資料管理方式與處理它的程式碼一樣靈活且快速的系統。這種平衡正是成功微服務策略的基礎。