在開發(fā)一個微服務(wù)之前,我們要設(shè)計微服務(wù)。設(shè)計微服務(wù)和領(lǐng)域驅(qū)動設(shè)計(DDD)有密切的關(guān)系,DDD有助于我們設(shè)計微服務(wù),所以我們這一節(jié)主要講下領(lǐng)域驅(qū)動設(shè)計的基本概念、建模方法、架構(gòu)等,以對我們設(shè)計微服務(wù)提供指導(dǎo)。 領(lǐng)域驅(qū)動設(shè)計DDD是目前比較火的一個軟件架構(gòu)術(shù)語,在我看來,這個其實是業(yè)務(wù)驅(qū)動IT的一個具體體現(xiàn)。DDD的核心思想是在做IT設(shè)計之前,要對業(yè)務(wù)有深入的理解。DDD告訴了我們領(lǐng)域建模的方法并理論化。 1. DDD的基本概念1)領(lǐng)域:領(lǐng)域就是一個組織所做的事情以及其中包含的一切,每個組織都有它的做事方式和業(yè)務(wù)范圍,這個業(yè)務(wù)范圍及在其中所進行的活動便是領(lǐng)域。對領(lǐng)域最了解的是業(yè)務(wù)專家,而不是IT人員。 2)模型:對領(lǐng)域的抽象和提煉,是對領(lǐng)域問題的思考過程的總結(jié),由業(yè)務(wù)分析人員完成。模型承上啟下:向上,業(yè)務(wù)分析人員需要用模型和業(yè)務(wù)專家溝通;向下,業(yè)務(wù)分析人員用模型指導(dǎo)軟件設(shè)計師進行設(shè)計并開發(fā)軟件。模型的首要要求是一致性,同一模型必須是一致的、保持不變的。 3)上下文:可以認(rèn)為是模型的勢力地盤,我的地盤我做主,大家要各司其職,不要越界。使用一個模型之前要進行哪些操作,使用一個模型之后要進行哪些善后。每個模型都有缺省的上下文,當(dāng)業(yè)務(wù)比較復(fù)雜,特別是要和存續(xù)系統(tǒng)對接時,模型的上下文邊界就很重要。 2. 領(lǐng)域驅(qū)動設(shè)計的戰(zhàn)略建模(原則) 也可以說是原則,但我更愿意稱之為戰(zhàn)略建模,就是在項目開始要從宏觀的層面劃分清楚業(yè)務(wù)領(lǐng)域,各業(yè)務(wù)領(lǐng)域能各自為政(限界上下文),獨立進化,對外又能形成一個統(tǒng)一的有機體(上下文映射) 1)限界上下文:把模型的上下文限定在一定范圍,模型負(fù)責(zé)哪些事情,不負(fù)責(zé)哪些事情有清晰的定義。界定模型的上下文的原因是保持模型的一致性,不會受外界因素的干擾。在《領(lǐng)域驅(qū)動設(shè)計》一書里,將限界上下文比喻成細(xì)胞膜,很形象:”細(xì)胞膜不僅僅能把細(xì)胞內(nèi)部和外部區(qū)分開,而且還能決定通過的物質(zhì)“。 一般根據(jù)下面的因素界定模型的上下文:團隊的組織結(jié)構(gòu)、現(xiàn)有的代碼庫模塊的劃分、數(shù)據(jù)庫的Schema等。推薦的做法是為每個領(lǐng)域創(chuàng)建一個獨立的模型,該模型的上下文是該領(lǐng)域或該領(lǐng)域的子集。 2) 上下文映射:描述不同的上下文之間的交互關(guān)系。業(yè)務(wù)領(lǐng)域之間不是老死不相往來,相反,業(yè)務(wù)領(lǐng)域之間存在各種各樣交互。交互有下面的類別: a)共享內(nèi)核:兩個或多個限界上下文共享同一模型子集及模型相關(guān)聯(lián)的邏輯、代碼。團隊之間需要統(tǒng)一共享,并且對共享內(nèi)核的修改要征得另外團隊的同意。 b)客戶-供應(yīng)商關(guān)系:兩個限界上下文是上下游的關(guān)系。處于下游的限界上下文(客戶)嚴(yán)重依賴于處于上游的限界上下文(供應(yīng)商)的輸出,比如在電商領(lǐng)域,報表限界上下文嚴(yán)重依賴于在線購物限界上下文的輸出(輸出會保存到數(shù)據(jù)庫里“客戶”和“供應(yīng)商”應(yīng)定時安排會議,”客戶“提交自己的需求,”供應(yīng)商“對”客戶“的需求進行排期開發(fā)?!肮?yīng)商”會開發(fā)一些自己不需要但是“客戶“需要的功能和接口。接口是聯(lián)系“客戶”與“供應(yīng)商”的紐帶,需要好好維護。 c)順從者:在客戶-供應(yīng)商關(guān)系里,當(dāng)供應(yīng)商沒有意愿滿足客戶的需求時,客戶只能被動接受供應(yīng)商提供的模型和接口。供應(yīng)商畢竟有自己要處理的事情,不可能靠“利他“理念推動供應(yīng)商一直滿足客戶的需求?!崩骸笆侨说奶煨?。對于“客戶”,可以走的一條路是自力更生,創(chuàng)建自己的模型滿足自己的需求,并逐步減少對“供應(yīng)商”的依賴。一旦”客戶“有了自己的模型,需要防崩潰層進行對”供應(yīng)商“提供的接口進行翻譯轉(zhuǎn)換。 d)防崩潰層/反腐層/緩沖層:當(dāng)客戶-供應(yīng)商關(guān)系變?yōu)椤表槒恼摺瓣P(guān)系,或者需要和存續(xù)系統(tǒng)進行交互時,需要防崩潰層對上游提供的信息和數(shù)據(jù)做出翻譯,類似于《設(shè)計模式》里的Adapter。“客戶”的防崩潰層通過調(diào)用“供應(yīng)商”提供的服務(wù)來獲取所需的數(shù)據(jù)或信息,經(jīng)過防崩潰層的翻譯、轉(zhuǎn)換、適配,轉(zhuǎn)變成和“客戶”端內(nèi)模型一致的數(shù)據(jù)。 e)隔離通道:當(dāng)兩個系統(tǒng)之間沒有或者很少交集,或者沒有上下游關(guān)系時,或者系統(tǒng)之間集成的難度很大而價值很小時,就可以考慮隔離通道。每個系統(tǒng)都可以用如一個大型企業(yè)里的人力資源領(lǐng)域、供應(yīng)鏈領(lǐng)域、社區(qū)領(lǐng)域。當(dāng)我們拿到一個新的需求時,我們應(yīng)該思考可否把它劃分為兩個或多個沒有相通之處的部分。如果可以,我們可以創(chuàng)建獨立的限界上下文,并獨立建模。 f)開發(fā)主機服務(wù):當(dāng)有上下游關(guān)系,不論是在客戶-供應(yīng)商或者順從者關(guān)系下,作為供應(yīng)商需要將和外面限界上下文交互的實體、邏輯包裝成服務(wù)開發(fā)出去。應(yīng)該定義一種協(xié)議,并將該協(xié)議開放出去,這樣其它上下文都可以通過該協(xié)議訪問。值得注意的是,當(dāng)有特殊的需求,需要客戶端自己采用防崩潰層進行翻譯,而不是擴展協(xié)議,以保證協(xié)議的簡單和連貫性。 g)提煉:抓住問題的本質(zhì)(主要矛盾),提煉業(yè)務(wù)的核心領(lǐng)域,圍繞核心領(lǐng)域投入重兵,其它的為支撐領(lǐng)域為核心領(lǐng)域提供支撐。業(yè)務(wù)分析人員、軟件設(shè)計人員應(yīng)對核心領(lǐng)域的細(xì)節(jié)多加關(guān)注,這些細(xì)節(jié)是系統(tǒng)成功的關(guān)鍵。 3. DDD的戰(zhàn)術(shù)建模戰(zhàn)術(shù)建模是指將不同的領(lǐng)域?qū)ο筮M行分類,并為之建立模型 1)實體:擁有唯一的標(biāo)識符;標(biāo)識符在生命周期里不會發(fā)生變化;其它屬性在生命周期內(nèi)可以發(fā)生變化;需要被跟蹤 實體是DDD里一個很重要的概念,在DDD的開始就要定義實體,圍繞實體開展工作。 實體可以采用普通舊式Java對象(PO JO,Plain Old Java Object)描述。 2)值對象:Value Object,在該對象的生命周期內(nèi),屬性不會有任何改變的對象;值對象是不可變的,是可以共享的。值對象應(yīng)該很小,也可以很簡單。 從實現(xiàn)上來講,一般是值對象設(shè)置為類的私有屬性,通過構(gòu)造函數(shù)傳入值對象具體的值,當(dāng)需要一個不同的值時,重新在構(gòu)造函數(shù)里傳入不同的值創(chuàng)建新的對象。 3)服務(wù)及服務(wù)對象:在領(lǐng)域建模的過程中,我們會發(fā)現(xiàn),有些活動不屬于任一個實體或值對象的職責(zé),反而實體或值對象是這些活動操作的對象,這些活動對整個領(lǐng)域來說有很重要,把這些活動安排到任何一個被操作的對象里,都會破環(huán)該實體或值對象。從面向?qū)ο缶幊痰囊暯?,我們會把這些活動歸類到一些對象里,這些對象叫服務(wù)對象,這些活動叫服務(wù)。 服務(wù)對象不具備內(nèi)部的狀態(tài),它的唯一目的是提供操作領(lǐng)域內(nèi)實體或值對象的活動。實體或值對象屬于服務(wù)的被操作對象,從語意上講,實體或值對象屬于賓語,而不是主語。例如:生成報告,這個活動里,報告屬于賓語,屬于被操作的對象,因而生成報告不屬于報告的職責(zé),要單獨創(chuàng)建一個服務(wù)對象來執(zhí)行這一活動。 服務(wù)也屬于領(lǐng)域模型的一部分,大部分的服務(wù)都會在DDD架構(gòu)模型里的領(lǐng)域?qū)印?/p> 4) 模塊:通過高內(nèi)聚、低耦合的原則將領(lǐng)域劃分為不同的模塊 實際操作中,一般把操作相同數(shù)據(jù)的或完成同一業(yè)務(wù)功能的部件劃分到一個模塊 模塊的劃分對于設(shè)計來說粒度還是太粗,所以需要將模塊繼續(xù)細(xì)分到聚合和聚合根 5) 聚合和聚合根:聚合用來定義對象的所有權(quán)和邊界。 聚合:針對數(shù)據(jù)變化可以考慮成一個單元的一組關(guān)聯(lián)的實體和值對象。比如,要刪除時,會一起被刪除;要創(chuàng)建時,會一起被創(chuàng)建。 聚合使用聚合根將內(nèi)部和外部的對象劃分開來,聚合根是一個實體,是外部可以訪問的唯一對象。 劃分聚合時要考慮的原則: a)數(shù)據(jù)的一致性:針對數(shù)據(jù)變化可以考慮成一個單元。執(zhí)行上,聚合外部的對象只持有對根的引用,而不能直接對聚合內(nèi)的實體進行訪問和更改。對聚合內(nèi)部的實體和對象的訪問必須通過根對象。在一個聚合內(nèi)部,根可以修改其它的實體,但這是可控的。如果根從內(nèi)存中被刪除,聚合內(nèi)的其他對象也將被刪除。如果一個聚合中的對象被保存在數(shù)據(jù)庫里,通過查詢來獲得的只有根對象,其他的對象只能通過從根對象出發(fā)的導(dǎo)航關(guān)聯(lián)關(guān)系獲取 b)聚合應(yīng)盡量小,盡量簡單和容易理解,而不是盡量完整。所以,對于聚合,一個工作思路就是對聚合內(nèi)實體的關(guān)系進行簡化,盡量把關(guān)系對應(yīng)成1對1的關(guān)系,比如通過刪除非基本的關(guān)聯(lián)關(guān)系、增加約束等方式 6) 工廠:創(chuàng)建對象的方法,將聚合作為一個整體來進行創(chuàng)建??梢詤⒁姟对O(shè)計模式》里的工廠模式或者抽象工廠模式。 工廠也是領(lǐng)域設(shè)計中的一類對象,工廠提供一個接口封裝復(fù)雜的封裝過程。 當(dāng)聚合里的實體組裝很簡單,或者一個對象的創(chuàng)建不會涉及到其它對象的創(chuàng)建,可以采用根實體的構(gòu)造函數(shù)創(chuàng)建。 7) 資源庫:也是模型設(shè)計里的一個對象,用來保存可用的聚合,并返回聚合根的引用。注意資源庫與存儲區(qū)的區(qū)別。資源庫屬于領(lǐng)域區(qū),封裝了對數(shù)據(jù)操作的細(xì)節(jié),對外部提供一個接口,隱藏了對數(shù)據(jù)操作的細(xì)節(jié)。而存儲區(qū)指數(shù)據(jù)庫、文件等持久化存儲設(shè)備,處于基礎(chǔ)架構(gòu)區(qū)。 工廠和資源庫、存儲區(qū)的關(guān)系: 4. DDD的多層架構(gòu)同其它架構(gòu)方法類似,DDD也是多層架構(gòu)。DDD的多層架構(gòu)分為了:1)UI層;2)應(yīng)用層;3)領(lǐng)域?qū)樱?)基礎(chǔ)架構(gòu)層。理解這幾個層級,對于下面開展微服務(wù)的工作很重要,所以要搞清楚這幾個層級的定位。 1)UI層,顧名思義,就是用戶界面層,用以提供和和用戶交互的界面。 2)應(yīng)用層:這個比較容易和其它架構(gòu)里提到的應(yīng)用混淆。在DDD里的應(yīng)用層,主要負(fù)責(zé)工作流程的編排工作。用服務(wù)的語言來講,應(yīng)用層可以稱為是服務(wù)編排(傳統(tǒng)的SOA)層或者服務(wù)調(diào)度層(微服務(wù))。 3)領(lǐng)域?qū)邮穷I(lǐng)域驅(qū)動設(shè)計中的一個很重要的層級,在這幾個層級中,只有領(lǐng)域?qū)迂?fù)責(zé)領(lǐng)域模型。領(lǐng)域?qū)影▽嶓w和業(yè)務(wù)邏輯。領(lǐng)域?qū)訉崿F(xiàn)了服務(wù)(實現(xiàn)業(yè)務(wù)邏輯)、資料庫(對數(shù)據(jù)實體進行CRUD操作)等 4)基礎(chǔ)架構(gòu)層:這個也和其它架構(gòu)里提到的基礎(chǔ)架構(gòu)有區(qū)別。這里的基礎(chǔ)設(shè)施不僅僅包含計算、網(wǎng)絡(luò)、存儲等硬件設(shè)施,還包括應(yīng)用的基礎(chǔ)架構(gòu),如操作系統(tǒng)、數(shù)據(jù)庫、消息隊列等。基礎(chǔ)架構(gòu)為上層邏輯的實現(xiàn)提供支撐,并存儲數(shù)據(jù) 對DDD的回顧就到這里,下一節(jié)我們會基于DDD的思想設(shè)計一個網(wǎng)上叫車的微服務(wù),開發(fā)并注冊到第一節(jié)開發(fā)的Eureka上,敬請期待 |
|