Skip to content

從單體到微服務的演進

前言

沒有哪個架構是「最好的」,只有「最適合當前階段的」。 從單體到微服務不是一步到位的跳躍,而是隨著業務規模和團隊規模增長,逐步演進的過程。過早拆分微服務和過晚拆分一樣危險。

這篇文章會帶你學什麼?

學完這章後,你將獲得:

  • 演進路徑:理解從單體到微服務的四個階段
  • 拆分時機:知道什麼時候該拆、什麼時候不該拆
  • 拆分策略:掌握按業務域拆分的方法論
  • 通訊模式:了解服務間同步和非同步通訊的選擇
  • 資料拆分:理解資料庫拆分的挑戰和方案
章節內容核心概念
第 1 章架構演進路徑單體→模組化→SOA→微服務
第 2 章拆分時機與原則Conway 定律、團隊自治
第 3 章拆分策略DDD 限界上下文、絞殺者模式
第 4 章服務通訊REST、gRPC、訊息佇列
第 5 章資料拆分資料庫拆分、資料同步

1. 架構演進路徑

架構演進不是技術驅動的,而是組織規模驅動的。當團隊從 5 人增長到 500 人時,單體架構的協作效率會急劇下降。

階段架構團隊規模特點
起步期單體應用1~10 人所有程式碼在一個專案中,部署簡單
成長期模組化單體10~50 人程式碼按模組劃分,但仍然一起部署
擴張期SOA(面向服務)50~200 人按業務線拆分為粗粒度服務
規模期微服務200+ 人細粒度服務,每個團隊獨立開發部署
Architecture Evolution Path
Click each stage to inspect its architecture characteristics
1
Monolithic architecture
2
Modular monolith
3
Service-oriented architecture
4
Microservices architecture
Monolithic architecture
All features are packaged in one application and share one database. It is simple and suitable for early rapid iteration.
User module
Order module
Payment module
Product module
Monolith app (one process)
MySQL
Suitable scale:Team < 10 people, DAU < 100k
Core challenge:Code is tightly coupled; a bug in one module may bring down the whole system

Conway 定律

「設計系統的組織,其產生的架構等同於組織的溝通結構。」——Melvin Conway

簡單說:3 個團隊做一個系統,最終會變成 3 個服務。架構拆分本質是組織拆分

反向 Conway 定律:既然組織結構決定了系統架構,那麼想要什麼樣的架構,就先調整成什麼樣的組織結構。比如你想拆出獨立的支付服務,就先組建一個獨立的支付團隊。很多公司微服務拆分失敗,不是技術問題,而是組織沒有跟著調整。


2. 什麼時候該拆微服務?

不是所有系統都需要微服務。過早拆分會帶來不必要的複雜性。

訊號說明建議
部署衝突頻繁多個團隊改同一個程式碼庫,經常衝突考慮拆分
某模組需要獨立擴容搜尋模組需要 10 倍於其他模組的資源考慮拆分
技術棧需要差異化AI 模組用 Python,主站用 Java考慮拆分
團隊 < 10 人溝通成本低,單體足夠不要拆
業務還在探索期需求變化快,邊界不清晰不要拆
沒有 DevOps 能力沒有 CI/CD、容器化、監控體系不要拆

3. 拆分策略

3.1 按業務域拆分(DDD 限界上下文)

DDD(領域驅動設計)的限界上下文(Bounded Context)是拆分微服務的最佳指導原則。每個限界上下文對應一個獨立的業務域,有自己的資料模型和業務規則。

什麼是限界上下文? 同一個詞在不同業務域中含義不同。比如「使用者」在使用者域是指註冊資訊(姓名、信箱),在訂單域是指下單人(收貨地址、支付方式),在推薦域是指行為畫像(瀏覽歷史、偏好標籤)。限界上下文就是劃定一個邊界,在這個邊界內,術語和模型有明確統一的含義。

┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│   使用者域     │  │   訂單域     │  │   支付域     │
│             │  │             │  │             │
│ User        │  │ Order       │  │ Payment     │
│ Profile     │  │ OrderItem   │  │ Refund      │
│ Address     │  │ Cart        │  │ Transaction │
│             │  │             │  │             │
│ 使用者服務    │  │ 訂單服務     │  │ 支付服務    │
└──────┬──────┘  └──────┬──────┘  └──────┬──────┘
       │                │                │
       └────── API 呼叫 / 事件通訊 ───────┘
限界上下文核心實體對應服務
使用者域User、Profile、Address使用者服務
商品域Product、Category、SKU商品服務
訂單域Order、OrderItem訂單服務
支付域Payment、Refund支付服務
物流域Shipment、Tracking物流服務

3.2 絞殺者模式(Strangler Fig Pattern)

不要一次性重寫整個單體,而是像絞殺榕一樣,逐步用新服務替換舊模組:

  1. 在單體外部建立新服務
  2. 透過代理層將部分流量路由到新服務
  3. 驗證新服務穩定後,逐步遷移更多流量
  4. 最終完全替換舊模組

4. 服務通訊模式

方式協定特點適用場景
RESTHTTP/JSON簡單通用,生態好對外 API、CRUD 操作
gRPCHTTP/2 + Protobuf高效能,強型別內部服務間高頻呼叫
訊息佇列AMQP/Kafka非同步解耦,削峰填谷事件通知、非同步任務
GraphQLHTTP/JSON客戶端按需查詢BFF 層、行動端

同步 vs 非同步的選擇

  • 需要立即回傳結果 → 同步(REST/gRPC)
  • 不需要立即回傳 → 非同步(訊息佇列)
  • 一個事件觸發多個動作 → 非同步(發布-訂閱)

經驗法則:能非同步就非同步,同步呼叫鏈越長,系統越脆弱。


5. 資料拆分:最難的部分

微服務拆分中最痛苦的不是程式碼拆分,而是資料庫拆分。每個服務應該擁有自己的資料庫,但這意味著跨服務查詢變得困難。

挑戰描述解決方案
跨服務 JOIN不能直接 JOIN 兩個服務的表API 組合查詢、資料冗餘
分散式事務跨庫事務無法用本地事務Saga、本地訊息表
資料一致性多個服務的資料可能暫時不一致最終一致性、事件驅動
資料遷移從共享庫遷移到獨立庫雙寫過渡、資料同步工具

總結

從單體到微服務是一個漸進的過程,不是一蹴可幾的革命。

回顧本章的關鍵要點:

  1. 演進路徑:單體→模組化單體→SOA→微服務,每一步都有明確的驅動力
  2. 拆分時機:團隊規模、部署衝突、擴容需求是拆分的訊號
  3. 拆分策略:用 DDD 限界上下文指導拆分,用絞殺者模式漸進遷移
  4. 通訊選擇:能非同步就非同步,同步呼叫鏈越短越好
  5. 資料拆分:最難但最重要,接受最終一致性是關鍵心態轉變

延伸閱讀