上一章是真的不好寫,吃奶的勁兒都快使出來了。本章計劃是查缺補漏,對BC的內容進行補充。您也看到了,戰略設計作為DDD中最重要的一部分,只寫一節就完事兒也差點意思。不過您也別期望太多,我們這個文章本來就是自身經驗的總結性,我是假設您有一定的DDD基礎,所以不會按照書的那種程度去寫,否則還不如直接看書呢。再說了,文章的讀者什麼層級都有,我要是再囉裡囉嗦把書的內容都事無鉅細的都寫下來一是會造成您閱讀的負擔,二是沒新意讀起來也沒勁。哪沒有類似的東西啊非得讀我寫的。廢話不多說,走起。
一、BC中到底包含哪些內容?
前面我們已經提過,BC其實更偏向於分析模型,還尚未到達系統的開發階段。即便如此,一旦開始了BC的設計,就需要考慮其應該包含哪些內容。我們們本章就這項內容進行一下系統介紹。噢,對了,有一個事情忘了說了。您在進行BC設計的時候必須要以BC為基本單位啊,千萬別陷入每個實現類或介面的細節中,那種東西最多組個小會兒就OK了。
1、BC設計階段
如果您是老讀者,應該看到前面我畫過一些圖,那個可真不是全量的BC內容。BC設計要包含三項任務:1)定義,即有哪些BC;2)識別BC中的領域模型是什麼;3)確認BC間的關係是什麼。而前面我只提到了BC的定義,實際上2和3兩項也是非常重要的兩點,請務必重點關注。
領域模型:這裡面的領域模型可不是指類或介面,更多的是指如業務實體、實體間的關係、業務流程定義(設計階段我比較喜歡用時序圖建模,您呢?)、業務說明文件等。舉一個已經臭了市的案例:論壇。下圖為論壇系統中“貼子管理”限界上下文的分析過程中所定義的兩個實體和實體的關係。業務流程定義不畫了,建議使用泳道圖來為業務流程建模,顯得專業。
並非領域模型的定義必須細化到業務實體的層次,在實體很多、關係複雜時到達這個粒度還是很有必要的,具體需要看業務的複雜度來決策(又是視情況而定,是不是準備拍桌子了?您不覺得這很正常嗎?做一個合格的研發人員必須帶著腦子工作,誰再說自己是碼農我跟誰急。對自己的工作都不尊重,你還想別讓人尊重您?)。針對領域模型的定義,這裡面有一條非常重要的原則:您定義的領域模型必須以BC為邊界且不能有任何歧義,一旦完成定義,涉及此BC的所有人員包括客戶、開發、需求等,都應該明白您講的領域模型的精確含義。
關於BC間的關聯關係,書上說的有點暈,把我都給整不會了。您先看一下下面的圖,有一個感性的認識。這東西在書上“Context Map”,翻譯成中文叫……,我們還是叫“Context Map(簡稱:CM)”吧,免得亂。總而言之,CM定義了BC及BC間的互動模式。“OHS/PL”說白了就是指服務提供方以什麼協議提供其介面供消費者使用。想傳輸速度快搞Dubbo、gRPC,想簡單易懂搞RESTful,如果是老一點的系統就整Web Service(我是真心不推薦再使用這個協議了,消費者老難受了)。反正不管搞什麼必須結合您公司的背景及技術普及度,比如您非得整一個COM+,那我懷疑您這幾年可能幹得就不是IT的事兒……
ACL一般指“防腐層”,這可是大用啊。在系統的互動場景中,被呼叫方所提供的資料一般需要轉換成強型別資料比如DTO或者對呼叫結果進行驗證,這類工作就是防腐層要乾的。所以我認為,但凡需要與其它BC互動,就必須得有一個ACL層,也強烈推薦建立這樣的開發規範。
至於說在CM中其它的規則,比如什麼“合作(Partner)”啊、“尊奉者(Conformist)”啊,您知道有這麼個東西就行了,也不必考慮這麼多。畢竟現實中的關係更為複雜,合作仍然需要以友好為前提,萬事是可以商量的。針對“共享核心”,特殊情況下也許可以用。現實中當做類庫還湊合,也不是特別推薦,改起來忒麻煩。要是一個團隊還能說得過去,一旦跨團隊就容易拉跨。圖中的U表示上游(Upstream),D表示下游(Downstream),說白了誰呼叫誰。呼叫方是下游,服務提供方是上游。針對BC間的互動我們下面會專門開一個小節做詳細展開說明。
2、BC的物理結構所含內容
1小節說所的內容主要是指在巨集觀上面BC的邏輯定義和設計。當進入到BC的物理設計階段後也需要限制其所包含的內容(說白了就是從程式碼的角度看每個BC中要包含的東西)。事實上,我們後續所討論的各類物件比如“聚合”、“領域服務”都是以BC為單位的,也就是某個BC內有什麼物件。一般來說,BC內會包含三類物件:基礎設施、領域開發模型(特指類、介面等,也就是在開發階段中設計的各種型別)和UI,讓我們細聊一下。
- 基礎設施:一般是指用於完成如遠端呼叫、資料庫訪問、訊息處理等各類與業務無關的服務或工具。以當前IT界的技術發達度,幾乎所有的基礎設施都有對應的工具或框架尤其是在Java圈,各類優秀的元件信手捏來便用。不說別的,光資料庫訪問類框架就快1萬個了。
- UI:現在不都是說微前端嗎?講究以微服務的方式落地前端。這種方式還有另外一個好處理:溝通成本低。一個團隊能完整的搞定一個垂直功能,不用天天和其它部門或團隊扯皮,少死多少億個腦細胞。不過按這種方式搞的時候您也得注意,需要有個能對UI進行全域性管理和拍板兒的帶頭大哥,別回頭8個團隊使用8個樣式,詐騙網站都比您這個專業。說句題外話 :據說有些類似搶票的系統,其架構比人家官方的架構還牛掰,你能不服?
- 領域設計模型:我特意加上“設計”兩個字,就是想強調這裡的模型是指類、介面這種的。DDD中強調使用“物件驅動設計(ODD)”的方式進行系統落地,以這種方式開發時領域模型種類數量也比較多,請參看如下圖。
重點! 在軟體設計階段也需要有建模過程,此階段的建模粒度更為細膩,要考慮至每一個型別。常使用的建模方式包含兩種:使用時序圖描述領域模型間的互動(哪個類呼叫哪個類的哪個方法),這叫“動態建模”;使用類圖表達實體定義的過程叫“靜態建模”。一般在開發階段的建模成果不需要形成很正式的文件,能說明問題即可。 |
三、BC的粒度
上一章中我們已經講過了BC的粒度,但我琢磨了一下,為了湊點字數還是得再說一次,這個東西過於靈活,很多時候需要依據主觀的判斷。說到這兒我又想吐槽兩句(我們寫文章的風格就是喜歡常態化的歪樓),您說為啥有人是高階程式設計師,有人是初級。我覺得不僅僅因為經驗,有些人幹了10年仍然也就是初級開發的水準。高階的人才需要有強烈的主觀能動性,一方面在行動上,另一方面在思維上。所以靈活不是障礙,這世上也不是好多事情都有規則可言的。再說了,幹我們這行的大部分人都上過大學(本人除外),從小到大考了那麼多的試哪一次不需要自己思考?怎麼一到工作上就不願意動腦了呢?有這種情況的人,趕緊去找個馬桶坐上面,左手一根兒煙右手一瓶酒,回顧一下所走過的工作人生,是否會為自己的碌碌無為而悔恨。
談到BC的粒度,可以按這麼一個層次開展:最小的為聚合根,最大的為所部署的系統。有人搞一個介面一個微服務,按技術區分不同的BC,這種情況多數是因為對效能的妥協。比如說您要搞共享單車,需要每輛車每5分鐘報告自己的位置,這裡的同步位置資訊介面出於效能考慮可以是一個服務一個。而正常的場景下面不建議這樣搞,因為在BC的劃分中,一個最重要的標準為“自治及完整”。過細的粒度會導致業務概念丟失。同樣,如果有可能也不建議使用太大的單體,這樣經常會導致在一個BC中出現概念衝突的情況,也就是書上說的“通用語言”混亂。假如您的團隊真的不是資源捉襟見肘,也儘量把整個系統分割一下,哪怕粒度粗一點也行,總比沒有強。軟體設計中經常會見到兩種狀態:一是幾乎沒有設計;一是設計過度,請您舉起雙手在內心中吶喊:“中庸、中庸、中庸”,這個是我們在設計時所追求的終極目標。
還有,還有,您在設計過程中要時刻的提醒自己:從業務角度進行劃分。我們是DDD,中文叫“領域驅動設計”,不是“技術驅動”或“頁面驅動”,您得始終圍繞著業務轉。我知道有些人是微服務的極端擁護者,這門技術先進性更強是無可厚非的。單體相對來最大的問題就是各模組無法獨立演化、系統內部關係混亂糾纏在一起。您可以在網上找到很多的微服務劃分規則,DDD也有自己的那一套這些我們前面也介紹過。這裡想重點提醒的是:您在劃分微服務的時候要注意,原來是大單體,不要出現劃分後變成了小單體的情況,要不然就是開發和運維的噩夢。原來一個大單體維護起來雖然麻煩點,但都在一個系統內。劃小後從物理上有了隔離,不僅定位問題更麻煩,開發間的協作難度也更大。解決這個問題的方式包括兩個方面:一是保證每一個BC的業務完整和自治;二是BC內部的各物件間的訪問要做好嚴格限制,具體規則後面會有詳解。
三、BC間的互動
BC的互動在前面的內容中大概寫了一些,尤其是引入了“Context Map(簡稱:CM)”的概念。因為BC間是物理的隔離,所以需要使用各類框架或協議實現遠端互動,生活在21世界的您應該也用過或至少知道一些遠端呼叫框架或工具(聽說過Feign不?)。總的來講,瞭解BC間的互動關係很重要,它可以幫助您從上到下去俯視整個系統的結構(多說兩句,我們日常工作中需要注意一下工作的方式方法,看待問題尤其如此。需要學會從兩個維度去看:一是高度,古語有云‘欲窮千里目,更上一層樓’,這裡面的深意您琢磨琢磨;二是廣度,下棋還知道走一步想三步呢,您工作也是這個理兒)。不過文章寫到這分兒上肯定不行,這沒法和您交待啊。誰都知道了解BC互動有用,書上也這麼說的,但問題是其作用到底有多大?
1、BC設計中定義互動的責任方
我平常辦事,就喜歡把責任這事兒事先說清楚了,說明白了,所謂“親兄弟明算帳”。這種方式挺容易得罪人,但我告訴你,前期不整清楚,後面打的架才厲害呢。職場可不是講兄弟友情的地方,這裡面涉及到各種利益比如工資利益、事故利益,職場即戰場,競爭很殘酷的。分明白了誰主責誰配合,出了問題大家日後也不會因此臉紅,先小人後君子沒毛病。說到這兒,您應該知道哪個重要資訊也需要寫在CM上了吧?“BC所屬團隊及負責人”。
2、BC定義了互動路徑
說到這個事情,估計您可能也有和我一樣的刻骨之痛。服務多了後,各種互動亂七八糟,沒人能講得明白。每人都各自只管自己那攤子事兒,誰都不負責。輕一點的也就是管理混亂,重一點的搞不好就是個事故,比如微服務的呼叫鏈中出現了業務環。有些時候,明明知道是上游服務出現了問題,可愣是找不到到底是哪個下游服務呼叫的。如果說都是部門內部的系統還好說。如果涉及到外部的呼叫呢?如果外部還特別的多呢?雖然說大部分情況可以通過企業閘道器進行限制,不過如果能在BC設計時有個路徑說明是不是會方便很多?後續想看看都和誰互動了,直接一上圖豈不美哉?當然了,這種理論上的東西誰都會說,想要到達這一步肯定需要對CM做實時維護,這就是愛美的代價。實踐中,如果不能保證所有細節,核心路徑至少還是需要的。
3、BC定義了關係
BC間的關係有兩種:上游和下游。A呼叫B:A是下游;B是上游。定義這種關係後,您如果對系統進行了修改,就能據此知道可能會影響哪個系統了。不過還是那句話,好是好,得有人維護CM才行。光靠腦子記也不是那麼回事兒,好記性還不如爛筆頭兒呢。一般來說,作為架構師還是需要有責任維護這麼一套東西的,同時也能約束專案組一旦涉及對外呼叫時需要提前和技術負責人說明,技術負責人再去修改BC設計文件形成一個良好的迴圈。這裡其實也涉及到一個如何處理與上司的關係的問題。假如您是個9級小領導,下面有10幾個人兒。一方面要管好手下,至少得有規矩;一方面您也得想想如何治理與上司的關係。最起碼您想幹什麼得和領導說說吧?這世道酒香也怕巷子深,會哭的孩子才有奶呢。想爭取點什麼您得讓老闆知道你乾的事兒值那個價兒。再說了,您一個9級小領導,上面那人也沒大到哪兒去,上有屁股下有臉的他比你還難受呢,啥事你不讓他知道不得被穿小鞋兒?再說了,如何與外部系統互動也是有規則、有管控的,您偷偷的就搞了,萬一出了事情你看領導不得把責任甩你身上。扯扯就遠了呢,我們這兒講DDD呢,您辛苦也快點把思緒拉回來。
針對BC間的互動方式,一種是常見的遠端服務呼叫如RESTful,另外一種是訊息佇列。如果條件允許,強烈推薦引入訊息佇列(此種方式不適用於需要主動獲取上游服務的資訊的場景)。DDD有一種稱之為事件驅動架構(EDA)的東西,簡單來說就是通過事件的方式推動業務流的前進。這種方式當然也適用於BC間的互動,因為事件本就是用於BC或聚合(具體概念後面細說)間的通訊的,只是我們不用“通訊”這個含義不明的詞,而是給這種通訊賦予一個業務概念即“事件”或“命令”。在BC間使用訊息佇列通訊基本上已經成為了一個事實上的標準,這種方式可以大大的減少系統間的耦合。
上面已經講了BC所應包含的三類資訊,就此打住不能再加其它的資訊了。現實的專案種類多樣,各團隊性質也很不同,本節的BC說明只是給出了一個理論指導,具個怎麼個用法及用到什麼程度要學會取捨。反正我個人地使用的時候,就定義出了BC、關鍵路徑和團隊資訊。至什麼上下游啊、詳細的呼叫關係啊完全沒用。主要是文件需要隨時維護這個事兒就是個不可能的任務。
四、經驗分享
個人針對BC設計的經驗大概分為三個方面:1)設計文件如何寫;2)領域模型如何提取;3)BC間的互動如何設計。
設計文件方面,建議使用層次化的方式編寫文件。現實中的系統要比上面的例子複雜的多,所以不可能使用一張圖就畫出所有的元素,那麼就先從總的架構去寫,然後逐漸分解到每一個BC中。以上面的說過的電商購物系統為例,請參看如下圖。從左到右一層層的進行細化。
其實還是可以再細化的,比如您可以在圖上標識出BC間到底使用哪種協議進行互動、領域模型各自的角色及互動細節(一般使用時序圖)等。具體要看業務的複雜度、團隊人力資源的配置程度等。記住一點:這東西不要強求。有些領導喜歡把所有的一切都文件化,最後還問開發你都幹什麼呢?怎麼進度這麼慢。就種情況你就把文件拍他臉上,讓看看謂的文件所帶來的資源損耗有多大,帶來的價值有多小。當然了,話是這麼說,有一個高層次的Context Map還是有必要的,至少讓團隊知道系統的整體結構是什麼樣的。
領域模型提取方面,如果您是團隊帶頭大哥同時還負責技術,找客戶或產品談的時候建議只用泳道圖展示業務流程足矣。別整什麼類圖、領域實體,你費了半天勁不說其實沒人關心這個,把自己還整得怪累的。書上說你需要和領域專家談BC的設計,總的方針是對的,但我們也得使用合適的方式。我可以很負責任的跟你說兩點:1)所謂的領域專家大部分只知道一個大致方向,有些時候他自己都不知道怎麼搞,完全是想當然的。這個從0到1的過程,就需要發揮您的三寸不爛之舌並結合自身的工作經驗,引導客戶把流程整明白就齊了,後續再優化還能再騙點專案款;2)大部分需求方的人也不懂什麼是領域實體,您就踏實的把他們的需求轉化成文件然後內部分析吧。至於在研發團隊內部,找一兩個高手按DDD戰術指導負責核心業務的設計;非核心或不是那麼重要的按工程師的技能水平和工作態度(在技術與態度間取捨時,我選擇後者)進行分配,只要不超出BC規定的圈子和團隊的規範、紀律,問題總不會大的。需要記住一點:DDD文化的培養成本非常非常高,不是一般企業能負擔的起的,更好的方式是建立規範,不服就直接斬首示眾。
BC間的互動,比較簡單。以現在的技術發展來看,如果使用Dubbo或Eureka等,框架內部已經規範了互動協議或使用第三方工具如Feigh基本也不再多考慮。遺留系統儘量使用HTTP的方式。另外,訊息佇列也是一種互動利器,在實現系統解耦的同時還可以大大提高系統的吞吐。個人推薦在業務系統中使用RabbitMQ,其超高的可靠性幾乎已經是金融類專案的標配。如果涉及對外的互動,建議您建立一個企業閘道器,一般也是通過HTTP的方式供外部消費。至於Web Service這類的,我感覺是過時的產物了。是不是有些嚴肅的系統強制要求使用WS不太清楚,我做的系統以垃圾居多。
寫在最後,BC的設計是一個系統“分”的過程。但企業級的系統都是“先分後合”。所謂“天下大勢,分久必合,合久必分”,您合計合計是不是這麼個意思。所以如果你能在“分”的階段做好,還用擔心“合”嗎?另外,即使是單體系統也並不影響我們實踐DDD指導,我們可以把BC定義為“包(Java)”或“名稱空間(.NET)”,並不是強制放在微服務層次。後續如果涉及到效能需求、擴充套件性需要、運維需求、安全需求等再做“分”的時候,因為前期遵循了BC設計原則,很快就能搞定。單體系統很優秀,可怕的是由於團隊缺少規矩和開發缺少責任感,造成系統內部都糾纏在一起。此種現象不僅是單體,微服務架構中也一樣,說白了就是大單體變成了小單體,本質上的差勁沒變化。