12.2 模組化單體的復興:幾時該拆、幾時該合#
單體回來了,但不是以前的單體#
2024–2026 年,業界出現一波「單體回歸」:Amazon Prime Video 把微服務合併回單體省下 90% 成本的故事廣為流傳,Kelsey Hightower 等人也直言「多數公司不需要微服務」。但回歸的不是當年那團泥球,而是模組化單體(Modular Monolith)。
模組化單體 = 一次部署的單體 + 微服務級的模組邊界:模組之間只能走公開介面、不能直連資料庫、各自獨立演進,保留將來拆出的選項。
flowchart TD
subgraph MM[模組化單體:一次部署]
API[API 層] --> M1[訂單模組]
API --> M2[商品模組]
API --> M3[會員模組]
M1 -.公開介面.-> M2
M1 --> DB1[(訂單表)]
M2 --> DB2[(商品表)]
M3 --> DB3[(會員表)]
end
NOTE[模組禁直連他表<br/>將來可整塊拆出]拆合準則:一張決策表#
| 维度 | 傾向合(模組化單體) | 傾向拆(微服務) |
|---|---|---|
| 團隊規模 | < 15 人、1~2 隊 | 多隊並行、需獨立發布 |
| 流量特徵 | 各模組流量同漲同跌 | 某模組需獨立擴縮 10 倍 |
| 事務需求 | 主流程強一致、本地事務 | 可接受最終一致、Saga |
| 技術異構 | 同一語言/框架即可 | 需多語言、多儲存 |
| 故障隔離 | 可接受整體重啟 | 需局部降級、熔斷隔離 |
| 運維能力 | 無專職 SRE | 有成熟 DevOps/SRE |
口訣:先合後拆、合而有界。預設從模組化單體開始,直到某個模組同時滿足「獨立擴縮 + 獨立發布 + 邊界清晰」才拆。
模組邊界的三條紀律#
- 模組只能調公開介面:禁止跨模組 import 內部類、禁止直連他人資料表。
- 模組有自己的測試邊界:每個模組可獨立單元測試 + 契約測試。
- 模組可整塊搬遷:打包方式讓某天能把模組原封不動搬成獨立服務或 Function。
以 Spring Modulith(Java 技術棧)為例:
# Spring Modulith 驗證模組邊界:違規引用直接失敗
./mvnw test -Dtest=ModularityTests
# 輸出範例:order 模組偷用了 product 內部類 -> 構建失敗
# Violation: order -> product.internal.ProductEntity
# 部署仍是單一 Deployment:運維成本與單體相同
apiVersion: apps/v1
kind: Deployment
metadata:
name: modular-shop
spec:
replicas: 3
template:
spec:
containers:
- name: app
image: shop:modular-2.4.0
ports:
- containerPort: 8080
演進路線:合 → 模組化 → 按需拆#
flowchart LR
S1[大泥球單體] --> S2[模組化單體<br/>劃界+禁直連] --> S3{哪個模組先成熟?}
S3 -->|高頻獨立擴縮| S4[拆出微服務]
S3 -->|低頻事件型| S5[拆成 FaaS 函數]
S3 -->|其餘| S6[留在單體內]實務上,淘寶教學模型裡的「評價」「優惠券過期提醒」這類低頻模組,2025 年後多半直接走向 Serverless(見 12.4 節),而不是拆成常駐微服務。
本章小結#
- 模組化單體 = 單體的運維成本 + 微服務的邊界紀律,是預設起點
- 拆合準則看六維:團隊、流量、事務、異構、隔離、運維
- 三條紀律:公開介面、測試邊界、可搬遷;用工具(如 Modulith 測試)強制執行
- 演進方向:合 → 模組化 → 按需拆成微服務或 FaaS
想一想#
- 「先合後拆」為什麼比「先拆後合」便宜?從重構成本與風險角度分析。
- 模組化單體的「禁直連他人資料表」紀律,是為了解決什麼未來問題?
- 假設你的訂單模組流量是其他模組的 20 倍且需獨立發布,你會拆嗎?對照六維準則逐項說明。