如何使用 DDD 指導微服務拆分?|8月更文挑戰

我是架構精進之路,大廠架構師,CSDN 博客專家,點擊上方「關注」,堅持天天爲你分享技術乾貨,私信我回復「01」,送你一份程序員成長進階大禮包。程序員

軟件架構發展經歷

軟件架構的發展經歷了從單體架構、垂直架構、SOA 架構到微服務架構以及到如今最新的 service mesh(網格服務架構)的過程。借用 dubbo 的網站架構發展圖和說明:數據庫

微服務存在的問題

進入微服務以後 , 解決了集中式架構的單體應用不少問題, 可是新的問題應運而生 , 微服務的力度應該多大 ?微服務如何設計呢?微服務如何拆分 ?微服務邊界在哪裏 ?markdown

很長時間人們都沒有解決這一問題,就連 Martin Fowler 在提出微服務架構的時候也沒有告訴咱們這該如何拆分微服務。架構

甚至在很長的時間裏人們對微服務拆分產生了一些誤解, 有人認爲:"微服務很簡單,就是將以前的單體應用拆分紅多個部署包, 或者將原來的單體應用架構替換爲一套支持微服務的技術架構,就算是微服務了。" 還有人認爲微服務應該拆分得越小越好。框架

鑑於上述情形, 不少項目由於前期拆分過分, 致使複雜度太高, 致使後期難以運維甚至難以上線。運維

能夠得出一個結論:微服務拆分困境產生的根本緣由就是不知道業務或者微服務的邊界到底在什麼地方。換句話說,肯定了業務邊界和應用邊界,這個困境也就迎刃而解了。dom

DDD 的誕生

而 DDD 就是解決了這個肯定業務邊界的問題,可見 DDD 並非一種技術架構,而是一種劃分業務領域範圍的方法論。DDD 的興起是因爲不少熟悉領域驅動建模(DDD)的工程師在進行微服務設計時, 發現用 DDD 的思路進行業務梳理能夠很好規劃服務邊界, 能夠很好實現微服務內部和外部的"高內聚、低耦合"。因而愈來愈多的人將 DDD 做爲業務劃分的指導思想。異步

DDD 是一種拆解業務、劃分業務、肯定業務邊界的方法, 是一種高度複雜的領域設計思想,將咱們的問題拆分紅一個個地域, 試圖分離技術實現的複雜性,主要解決的是軟件難以理解難以演進的問題,DDD 不是一種架構, 而是一種架構方法論, 目的就是將複雜問題領域簡單化, 幫助咱們設計出清晰的領域和邊界, 能夠很好的實現技術架構的演進。DDD 包括兩部分,戰略設計部分和戰術設計部分。分佈式

  • 戰略設計主要從業務視角出發,創建業務領域模型,劃分領域邊界,創建通用語言的限界上下文,限界上下文能夠做爲微服務設計的參考邊界。微服務

  • 戰術設計則從技術視角出發,側重於領域模型的技術實現,完成軟件開發和落地,包括:聚合根、實體、值對象、領域服務、應用服務和資源庫等代碼邏輯的設計和實現。

微服務拆分難題

開發者在剛開始嘗試實現本身的微服務架構時,每每會產生一系列問題 :

  • 微服務到底應該怎麼劃分?

  • 一個典型的微服務到底應該有多微?

  • 若是作了微服務設計,最後真的會有好處嗎?

回答上面的問題須要首先了解微服務設計的邏輯,科學的架構設計應該經過一些輸入並逐步推導出結果,架構師要避免憑空設計和「拍腦門」的作法。

服務的劃分有一些基本的方法和原則,經過這些方法能讓微服務劃分更有操做性。最終在微服務落地實施時也能按圖索驥,不管是對遺留系統改造仍是全新系統的架構都能遊刃有餘。

微服務拆分的幾個階段

在開始劃分微服務以前,架構師須要在大腦中有一個重要的認識:微服務只是手段,不是目的。

微服務架構是爲了讓系統變得更容易拓展、更富有彈性。在把單體應用變成靠譜的微服務架構以前,單體系統的各個模塊應該是合理、清晰地。

也就是說,從邏輯上單體系統和微服務沒有區別,某種理想狀況下微服務只是把單體系統的各個模塊分開部署了而已

大量的實踐教訓告訴咱們,混沌的微服務架構,比解耦良好的單體應用會帶來更多麻煩。

混亂的微服務VS良好的單體

領域驅動設計立足於面向對象思想,從業務出發,經過領域模型的方式反映系統的抽象,從而獲得合理的服務劃分。

採用 DDD 來進行業務建模和服務拆分時,能夠參考下面幾個階段:

  • 使用 DDD(領域驅動建模) 進行業務建模,從業務中獲取抽象的模型(例如訂單、用戶),根據模型的關係進行劃分限界上下文。

  • 檢驗模型是否獲得合適的的抽象,並能反映系統設計和響應業務變化。

  • 從 DDD 的限界上下文往微服務轉化,並獲得系統架構、API 列表、集成方式等產出。

使用DDD劃分微服務的過程

如何抽象?

抽象須要找到看似無關事務的內在聯繫,對微服務的設計尤其重要。

然而現實的例子比比皆是,電信或移動營業廳還須要用戶分兩步辦理號卡業務、寬帶業務。原始是不合適的抽象模型形成的,並最終影響了微服務的劃分。

咱們可使用概念圖來描述一些概念的抽象關係。

商品這一律唸的概念圖

若是沒有抽象出領域模型,就得不到正確的微服務劃分。

使用 DDD 進行業務建模

經過利用 DDD 對系統從業務的角度分析,對系統進行抽象後,獲得內聚更高的業務模型集合,在 DDD 中一組概念接近、高度內聚並能找到清晰的邊界的業務模型被稱做限界上下文(Bounded Context)。

限界上下文能夠視爲邏輯上的微服務,或者單體應用中的一個組件。

在電商領域就是訂單、商品以及支付等幾個在電商領域最爲常見的概念;在社交領域就是用戶、羣組、消息等。

DDD 的方法論中是如何找到子系統的邊界的呢?

其中一項實踐叫作事件風暴工做坊,工做坊要求業務需求提出者和技術實施者協做完成領域建模。把系統狀態作出改變的事件做爲關鍵點,從系統事件的角度觸發,提取能反應系統運做的業務模型。再進一步識別模型之間的關係,劃分出限界上下文,能夠看作邏輯上的微服務。

事件是系統數據流中的關鍵點,相似於電影製做中的關鍵幀。

例如系統管理員能夠登陸、建立商品、上架商品,對應的系統狀態的改變是用戶已登陸、商品已建立、商品已經上架;相應的顧客能夠登陸、建立訂單、支付,對應的系統狀態改變是用戶已登陸、訂單已建立、訂單已支付。

利用事件刺探業務黑盒並抽象出模型

在獲得模型以後,經過分析模型之間的關係得出限界上下文。例如商品屬性和商品相對於用戶、用戶組關係更爲密切,經過這些關係做出限界上下文拆分的基本線索。

其次是識別模型中的二義性,讓限界上下文劃分更爲準確。

例如,在電商領域,另一個不恰當設計的例子是:把訂單中的訂單項當作和商品一樣的概念劃分到了商品服務

但訂單中的商品實際上和商品庫中的商品不是同一個概念。當訂單須要修改訂單下的商品信息時,須要訪問商品服務,這勢必形成了訂單和商品服務的耦合。

合理的設計應該是:商品服務提供商品的信息給訂單服務,可是訂單服務沒有理由修改商品信息,而是訪問做爲商品快照的訂單項。

訂單項應該做爲一個獨立的概念被劃分到訂單服務中,而不是和商品使用同一個概念,甚至共享同一張數據庫表。

典型具備」二義性「陷阱的場景

一組關係密切的模型造成了上下文(context),二義性的識別能幫咱們找到上下文的邊界(bounded)。

驗證和評審領域模型

前面咱們說到限界上下文能夠做爲邏輯上的微服務,但並不意味着咱們能夠直接把限界上下文變成微服務。

限界上下文被設計出來後,驗證它的方法能夠從咱們採用微服務的兩個目的出發:下降耦合、容易擴展,能夠做爲限界上下文評審原則:

**原則 1:**設計出來的限界上下文之間的互相依賴應該越少越好,依賴的上游不該該知道下游的信息。

**原則 2:**使用潛在業務進行適配,若是能在必定程度上響應業務變化,則證實用它指導出來的微服務能夠在至關一段時間內足以支撐應用開發。

可是理想的領域模型每每抽象程度、成本、複用性這幾個因素中獲取平衡。

」抽象」的成本

用一個簡單的圖來表達話,咱們的領域模型設計每每在複用性和成本取得平衡的中間區域纔有實用價值。

幾個典型的誤區

在大量使用 DDD 指導微服務拆分的實踐後,咱們發現不少系統設計存在一些常見的誤區

主要分爲兩類:未成功作出抽象、抽象程度太高、錯誤的抽象。

1)未成功作出抽象

在實際開發過程當中,你們都有一個體會,設計階段只考慮了一些常見的服務,可是發現項目中有大量能夠重用的邏輯,並應該作成單獨服務。

當咱們在作服務拆分時,遺漏了服務的結果是有一些業務邏輯被分散到各個服務中,並不斷重複。

2)抽象程度太高

抽象程度太高最典型的一個特徵是獲得的限界上下文極端的微小。

抽象程度太高帶來的成本有:更多的微服務部署帶來的運維壓力、開發調試難度提升、服務間通訊帶來的性能開銷、跨服務的分佈式事務協調等。所以抽象不是越高越好,應根據實際業務須要和成本考慮。

那相應的,微服務到底應該多小呢?

業界流傳一句話來形容,微服務應該多小:「一個微服務應該能夠在二週內完成重寫「。

這句話可能只是一句調侃,若是真的做爲微服務應該多微的標準是不可取的。

微服務的大小應該取決於劃分限界上下文時各個限界上下文內聚程度。

3)錯誤抽象

對微服務或 DDD 理解不夠。模型具備二義性,被放到不一樣的限界上下文。

例如,訂單中的收貨地址、用戶配置的經常使用地址以及地址庫中的標準地址。

這三種地址雖然名稱相似,可是在概念上徹底不是一回事

假如架構師將」地址「劃分到了標準地址庫中,勢必會形成用戶上下文和系統配置上下文、訂單上下文存在沒必要要的耦合。

抽象錯誤帶來的依賴

上圖的右邊爲正常的依賴關係,左邊產生了不正常的依賴,會進一步產生雙向依賴。

從限界上下文到系統架構

在經過 DDD 獲得領域模型和限界上下文後,理論上咱們已經獲得了微服務的拆分。可是,限界上下文到系統架構還須要完成下面幾件事。

1)設計微服務之間的依賴關係

一個合理的分佈式系統,系統之間的依賴應該是很是清晰地。依賴,在軟件開發中指的是一個應用或者組件須要另一個組件提供必要的功能才能正常工做。所以被依賴的組件是不知道依賴它的應用的,換句話說,被調用者不須要知道調用方的信息,不然這不是一個合理的依賴

在微服務設計時,若是 domain service 須要經過一個 from 參數,根據不一樣的渠道作出不一樣的行爲,這對系統的拓展是致命的。例如,用戶服務對於訪問他的來源不該該知曉;用戶服務應該對訂單、商品、物流等訪問者提供無差異的服務。

所以,微服務的依賴關係能夠總結爲:上游系統不須要知道下游系統信息,不然請從新審視系統架構。

2)設計微服務間集成方式

拆分微服務是爲了更好的集成到一塊兒,對於後續落地來講,還有服務集成這一重要的階段。

微服務之間的集成方式會受到不少因素的制約,前面在討論微服務到底有多微的時候就順便提到了集成會帶來成本,處於不一樣的目的能夠採用不一樣的集成方式。

  • 採用 RPC(遠程調用) 的方式集成。

使用 RPC 的方式可讓開發者很是容易的切換到分佈式系統開發中來,可是 RPC 的耦合性依然很高,同時須要對 RPC 平臺依賴。業界優秀的 RPC 框架有 dubbo、Grpc、thrift 等

  • 採用消息的方式集成。

使用消息的方式異步傳輸數據,服務之間使用發佈-訂閱的方式交互。另一種思想是經過對系統事件傳遞,所以產生了 Event Sourcing 這種集成模式,讓微服務具有自然的彈性。

  • 採用 RESTful 方式集成。

RESTful 是一種最大化利用 HTTP 協議的 API 設計方式,服務之間經過 HTTP API 集成。這種方式讓耦合變得極低,甚至稍做修改就能夠暴露給外部系統使用。

這三種集成方式耦合程度由高到低,適用於不一樣的場景,須要根據實際狀況選擇,甚至在系統中可能同時存在。

服務間集成的方式還有其餘方式,通常來講,上面三種微服務集成的方式能夠歸納目前常見系統大部分需求。

總結

這篇文章主要研討了 DDD 火起來的緣由, 解決了什麼業界難題, 知道 DDD 主要思路 , 以及 DDD 大概的實現步驟等 。

邏輯每每比經驗更爲重要。寫這篇文章的初衷是爲了獲得微服務劃分的依據是什麼,我該怎麼有說服力的回覆?

是具體狀況具體分析?By experience?仍是說,我是經過一套方法對業務邏輯進行分析獲得的。

  • 當沒有足夠的經驗直接解決問題,或問題龐大到不足以使用經驗解決時,能支撐你作出決策就只有對輸入問題進行有效的分析。

  • 使用 DDD 指導微服務劃分,能在必定程度上彌補經驗的不足,作出有理有據的系統架構設計。

我是架構精進之路,大廠架構師,CSDN 博客專家,點擊上方「關注」,堅持天天爲你分享技術乾貨,私信我回復「01」,送你一份程序員成長進階大禮包。

相關文章
相關標籤/搜索