
微服務業務拆分規范與邊界設計老板說咱們把系統拆成微服務吧你二話不說把每個 Controller 拆成一個服務。第二天發現要用 30 個 Git 倉庫、40 個端口、50 個 Docker 容器你崩潰了。拆分不是數學除法拆分是一門如何優雅地切蛋糕的藝術。一、拆分前先問自己三個問題在動手之前請對著鏡子問自己團隊有幾個人3個人維護15個微服務 災難系統真的需要嗎日均PV不到1萬的單體改微服務 自找麻煩部署能力跟得上嗎微服務需要容器化、CI/CD、監控體系微服務不是銀彈。它解決的是組織復雜度和規模化問題但對小團隊、小系統而言它帶來的運維成本遠高于收益。二、核心原則三大鐵律2.1 單一職責原則SRP一個微服務只做一件事并且把它做好。不是一個Controller拆一個服務而是一個業務邊界對應一個服務。反面例子把用戶注冊、商品管理、優惠券三個無關功能塞進一個服務——改個優惠券規則要重啟整個服務炸得所有人登錄不了。2.2 限界上下文Bounded Context這是 DDD領域驅動設計的核心概念。簡單說就是在一個邊界里每個術語有唯一的、不產生歧義的含義。舉例電商系統里有商品這個詞——在商品域商品 名稱/描述/圖片/SKU用于商品展示在訂單域商品 下單時的快照/價格/數量用于訂單結算在庫存域商品 SKU編號/庫存量/倉庫位置用于庫存管理同一個詞在不同上下文里長得完全不同。如果不劃分限界上下文一個商品類就變成了萬能上帝類字段上百個誰都不敢改。2.3 高內聚、低耦合高內聚相關功能放在同一個服務里改一個需求只動一個服務低耦合服務間的依賴要少你改你的我完全不受影響判斷標準如果改功能A必然要改服務B那A和B應該在同一個服務里。三、DDD 快速入門DDD 不是玄學它的核心概念就四個概念通俗解釋舉例領域Domain整個業務范圍電商系統子域Subdomain拆分出來的子業務商品子域、訂單子域限界上下文Bounded Context一個明確邊界內部術語自洽訂單上下文中商品的含義聚合根Aggregate Root一組對象的老大外部只能通過它訪問內部Order聚合根內部有OrderItem拆分的方法論本質上就兩種按業務能力拆分從業務流程出發把端到端的能力拆出來更直觀、更推薦按子域拆分先識別核心子域和支撐子域再分別拆更DDD、更學術四、無人售貨柜項目拆分實戰以一個無人售貨柜項目為例演示從業務出發的拆分過程┌─────────────────────────────────────────────┐ │ 業務分析 → 識別業務能力 → 劃分限界上下文 → 獨立服務 │ └─────────────────────────────────────────────┘服務業務能力核心數據接口示例設備服務設備注冊、狀態監控、固件升級設備表、貨道表POST /api/device/openDoor商品服務商品管理、分類、SKU商品表、分類表GET /api/product/{id}訂單服務下單、支付回調、退款訂單表、訂單明細表POST /api/order/create支付服務微信/支付寶對接、對賬支付流水表POST /api/pay/callback用戶服務注冊、登錄、會員、積分用戶表、會員表GET /api/user/profile庫存服務庫存扣減、補貨預警庫存表、庫存流水PUT /api/inventory/deduct拆分后每個服務有自己的數據庫業務邊界清晰團隊可以并行開發。五、服務邊界的黃金規則什么應該放一起強數據一致性需求的操作下單扣庫存用 Saga 事務不是強一致但放在同一個服務里用本地事務就很香頻繁協同修改的數據共享核心業務邏輯什么必須拆開不同變化頻率的功能用戶模塊很少改訂單模塊天天改不同性能要求的功能查詢要快 vs 寫入要準不同團隊負責的功能康威定律系統架構反映組織架構六、數據庫拆分原則鐵律每個微服務獨占數據庫禁止跨庫 Join。這不是 Redis 的橫向擴容這是硬性約束。一旦你允許跨庫 Join兩個數據庫就耦合了微服務的所有好處全沒了——你拆了半天又回來一個大單體。替代方案需求方案示例訂單需要商品名稱API 調用orderService → productService訂單狀態變更通知庫存MQ 異步事件訂單支付成功 → 發MQ → 庫存服務出庫報表需要跨服務聚合數據CQRS 讀寫分離從多個服務采集數據到只讀報表庫用戶信息高頻關聯數據冗余訂單表存 userId userName允許少量不一致互聯網的真實世界里數據冗余是常態。訂單表里存一份商品快照當時的標題、價格不怕不一致反而避免了每次查單都要調商品服務。七、API 設計規范RestControllerRequestMapping(/api/v1/orders)publicclassOrderController{PostMappingpublicResultOrderVOcreateOrder(ValidRequestBodyCreateOrderRequestreq){// 冪等同樣的請求多次執行結果一樣// 不做冪等的 POST請求超時重試可能創建兩條訂單}GetMapping(/{orderId})publicResultOrderVOgetOrder(PathVariableLongorderId){// RESTful資源用名詞操作用 HTTP 方法}GetMappingpublicResultPageOrderVOlistOrders(RequestParamIntegerpage,RequestParamIntegersize,RequestParam(requiredfalse)Stringstatus){// 版本管理/api/v1/ 前綴防止破壞性變更}}冪等設計是微服務 API 最容易被忽視的要點。用戶網絡不好點了兩次支付你總不能讓人家扣兩次錢。做法請求帶上唯一冪等鍵服務端判斷重復直接返回第一次的結果。八、拆分粒度的權衡粒度特征問題太粗沒拆干凈一個服務 20 張表、50 個接口改一行代碼重啟整個世界太細拆過頭了一個接口一個服務運維成本爆炸調用鏈繞地球一圈適中一個服務 3-10 張表獨立部署團隊能 hold 住運維成本可控過度拆分的反模式一個 Controller 拆一個服務綜合征所有服務共享同一個數據庫偽微服務大量同步 HTTP 調用形成分布式單體九、演進路線階段1單體應用 ↓ 業務增長團隊擴大 階段2適度拆分3-5個核心服務 ↓ 能力成熟工具鏈到位 階段3精細化拆分按業務需求繼續細化不需要一開始就拆得天花爛。可以先把變化劇烈、性能敏感的模塊拆出來比如訂單、支付其他模塊留在單體里慢慢遷。這就是 Martin Fowler 說的絞殺者模式——新功能在微服務里做舊功能逐步替換。小結微服務拆分不是技術問題是業務理解問題。核心口訣看業務邊界而不是看代碼行數看變化頻率而不是看表有多少張看團隊結構而不是看文件夾數。拆分之前先畫一張限界上下文圖這比寫 100 行配置更有價值。