架构的“一小步”,业务的一大步
前言:
談到“架構(gòu)”這兩個字,會有好多的名詞閃現(xiàn),比如:分層架構(gòu)、事件驅(qū)動架構(gòu)、DDD、CQRS等。亦或者一堆的軟件設(shè)計原則,如:KISS原則(Keep it Simple and Stupid)、SOLID原則(單一責(zé)任原則、開放封閉原則、里氏替換原則、接口分離原則、依賴導(dǎo)致原則)等。甚至如狀態(tài)圖、用例圖、時序圖、活動圖等UML建模,GOF設(shè)計模式等。
本文不會討論這些架構(gòu)概念,而是從閑魚詳情頁這個業(yè)務(wù)場景出發(fā),分析出當(dāng)前的業(yè)務(wù)問題和痛點,然后通過一步步的架構(gòu)推導(dǎo)設(shè)計,解決這些痛點。隨著業(yè)務(wù)的發(fā)展,相信這些問題大家都會遇到。而解決問題的過程,或多或少的會用到上面的設(shè)計原則。
一:老的業(yè)務(wù)架構(gòu) - MVC架構(gòu)
很多同學(xué)開始寫業(yè)務(wù)的時候,基本都會先建表,然后生成CURD,最后再堆業(yè)務(wù)邏輯,從DAO->Manager->Service->Controller一路寫到底。在業(yè)務(wù)小的時候,這種架構(gòu)非常的簡單實用,可以快速的開發(fā)上線。
但隨著業(yè)務(wù)發(fā)展,人員不斷增加,老的架構(gòu)難以支撐業(yè)務(wù)的發(fā)展,穩(wěn)定性和效率受到極大挑戰(zhàn)。蠻荒時代已過,精耕細作的時代到來,急需一種更合適的架構(gòu)來支撐業(yè)務(wù)的發(fā)展。
以閑魚的詳情頁舉例,在業(yè)務(wù)初期,詳情的樣式只有普通的開價寶貝一種,但隨著業(yè)務(wù)的發(fā)展,演變出拍賣、免費送、租房、玩家等細分領(lǐng)域的商品詳情頁(我們將細分領(lǐng)域的業(yè)務(wù)命名為“垂直業(yè)務(wù)”)。
圖2:閑魚詳情頁業(yè)務(wù)演變
?此時,還不斷的在老的業(yè)務(wù)邏輯里添加新的業(yè)務(wù)邏輯,導(dǎo)致所有的詳情業(yè)務(wù)邏輯堆在一起。于是乎,會出現(xiàn)下面的場景:
1)今天A詳情業(yè)務(wù)線的同學(xué),加了段邏輯,掛了,影響了所有業(yè)務(wù)線的同學(xué);
2)B詳情業(yè)務(wù)線的同學(xué)想做單獨的監(jiān)控、緩存、降級等,做不到啊,大家的邏輯在一起,改造成本太高;
3)C詳情業(yè)務(wù)線的同學(xué)本想只關(guān)注C詳情業(yè)務(wù)邏輯,發(fā)現(xiàn)所有業(yè)務(wù)都在一起,不得不將所有業(yè)務(wù)都理清楚一遍;
4)D詳情業(yè)務(wù)線的同學(xué)發(fā)現(xiàn)前面的業(yè)務(wù)邏輯太復(fù)雜,為了將影響面減少到最小,找了一個認為最安全的地方,加了一段D詳情業(yè)務(wù)的特殊處理。
圖3:詳情頁堆邏輯代碼
有人可能會問,堆邏輯正常的啊,加幾行代碼,業(yè)務(wù)就上線了,互聯(lián)網(wǎng)提倡的敏捷開發(fā),當(dāng)然是怎么快怎么來。但敏捷開發(fā) != 提需求 + 編碼 + 發(fā)布,加幾行代碼交付業(yè)務(wù)上線,可能會帶來眼前的收益,但一直這么下去,代碼會越來越臃腫,沒有設(shè)計和文檔沉淀的系統(tǒng),難以維護,出故障只是時間問題。
二:新的業(yè)務(wù)架構(gòu) - 業(yè)務(wù)隔離 + 領(lǐng)域建模
吉德林法則講:把難題清清楚楚地寫出來,便已經(jīng)解決了一半。 老的架構(gòu)的問題,歸納起來講:
1.業(yè)務(wù)沒有做隔離,所有的垂直業(yè)務(wù)邏輯都堆在一起,互相影響。
2.詳情頁業(yè)務(wù)足夠的復(fù)雜,卻沒有統(tǒng)一的模型,形成統(tǒng)一的認知。
因此,架構(gòu)的設(shè)計方案就著重解決這兩個問題。
2.1 業(yè)務(wù)隔離架構(gòu)推導(dǎo)與設(shè)計
一個業(yè)務(wù),有多種形態(tài)的實現(xiàn),很容易對應(yīng)到設(shè)計模式里的策略模式。最粗暴的方式,每個垂直業(yè)務(wù)都自己實現(xiàn)詳情頁。
圖4 使用策略模式,隔離每個業(yè)務(wù)的實現(xiàn)
這種方式,業(yè)務(wù)雖然隔離了,但維護成本極高,添加一個通用的功能,所有業(yè)務(wù)都需要添加一遍。 因此需要將共性內(nèi)容(不變的部分)抽象出來,將變化的部分由各個垂直業(yè)務(wù)去實現(xiàn)。
圖5,抽象出共性(不變)和特性(變)的內(nèi)容
這種方式,解決了業(yè)務(wù)的隔離,共性的內(nèi)容統(tǒng)一維護,變化的部分由各個垂直業(yè)務(wù)獨立維護。但此時,所有的業(yè)務(wù)團隊還是在一個應(yīng)用工程里寫業(yè)務(wù)代碼,會出現(xiàn)如下場景:
1)開發(fā)階段,各業(yè)務(wù)線都在這個應(yīng)用里拉取一個分支進行開發(fā),集成部署時,代碼沖突難以避免。
2)A業(yè)務(wù)線添加了一段自己業(yè)務(wù)線的邏輯,部署失敗了,導(dǎo)致其它業(yè)務(wù)線也無法使用。
3)N業(yè)務(wù)線不在自己的團隊內(nèi),屬于外部合作團隊,如何添加該業(yè)務(wù)線的邏輯。
這些場景存在的原因在于,業(yè)務(wù)代碼雖然隔離了, 但人員的開發(fā)過程并沒有隔離。有如下3中方法可供選擇:
a. 將共性的內(nèi)容打成二方依賴包,每個垂直業(yè)務(wù)依賴這個二方包,進行獨立應(yīng)用開發(fā)。
這種二方依賴的方法最常用,但在二方服務(wù)里添加一個通用功能的時候,要告知所有業(yè)務(wù)方都升級二方包,還要發(fā)版,升級的成本高。
圖6.a 共性內(nèi)容打成二方包
b. 垂直業(yè)務(wù)獨立應(yīng)用開發(fā),然后將代碼打成jar包,再集成到共性業(yè)務(wù)應(yīng)用里,一起部署。
該方法依賴關(guān)系跟方法a相反,但部署方式不夠靈活。如果要實現(xiàn)垂直業(yè)務(wù)的獨立部署,改造成本太高,需要做類隔離,budle隔離等。
圖6.b 垂直業(yè)務(wù)打成二方包
圖6.c? 互不依賴,部署方式可選
至此,業(yè)務(wù)的隔離,開發(fā)人員的隔離問題都已解決。但該架構(gòu)方案顯然不只有詳情這一個場景可用,其他類似的業(yè)務(wù)場景也有相似的問題。因此,架構(gòu)的代碼不能與業(yè)務(wù)的代碼耦合在一起,需要將架構(gòu)的代碼獨立出來,形成通用的技術(shù)工具,以應(yīng)用于所有類似的業(yè)務(wù),業(yè)務(wù)開發(fā)應(yīng)該只關(guān)心業(yè)務(wù)的事情。我們特地為此開發(fā)了一個多實現(xiàn)“業(yè)務(wù)隔離”路由工具,最終的隔離架構(gòu)設(shè)計圖如下:.
圖7: 總體架構(gòu)圖。
2.2.領(lǐng)域模型在詳情頁的使用
隔離的問題解決了,再來談?wù)勗斍轫摰念I(lǐng)域建模。
為何需要領(lǐng)域建模?好多java開發(fā)的同學(xué),大都會遇到這樣的問題:1)一門OO(面向?qū)ο?的語言,寫出來的代碼都沒有OO的感覺,到像是過程式的代碼,面向?qū)ο蟮乃枷牖緵]有使用到。2)雖然代碼滿足了業(yè)務(wù)需求,但從代碼中,完全看不到業(yè)務(wù)領(lǐng)域的影子,業(yè)務(wù)領(lǐng)域和代碼是脫節(jié)的。3)隨著業(yè)務(wù)的越來越復(fù)雜,里面的依賴關(guān)系梳理起來非常困難,業(yè)務(wù)模塊沒有邊界可言。
為了不給后人挖坑,為了解決詳情頁復(fù)雜的邏輯,為了讓代碼更有范,為了讓接手詳情的同學(xué)都有統(tǒng)一的業(yè)務(wù)領(lǐng)域認知,因此決定對詳情領(lǐng)域進行領(lǐng)域建模。
圖8: 領(lǐng)域驅(qū)動設(shè)計-模型關(guān)系總圖
Eric Evans的那本《領(lǐng)域驅(qū)動設(shè)計——軟件核心復(fù)雜性應(yīng)對之道》經(jīng)典書籍大行其道十幾年,網(wǎng)上關(guān)于領(lǐng)域建模的文章也是浩如煙海,自頂向下、自底向上、四色原型建模、問題空間領(lǐng)域模型抽象方法論也非常的多。但這些文章和書籍,要么談?wù)摾碚摳拍?#xff0c;要么談?wù)摻7绞?#xff0c;對初學(xué)者來說,看完之后,還是寫不出相應(yīng)的代碼。
所以本文不在重復(fù)的去講領(lǐng)域建模的概念,直接通過閑魚詳情頁這個業(yè)務(wù)場景,講述建模的步驟、DDD的代碼展示,給讀者一個更直觀的參考。
2.2.1 詳情頁領(lǐng)域建模
閑魚詳情頁是一個純展示的頁面,用一句話可以概括為,“詳情頁是包括:商品、賣家、買家、魚塘、認證、互動等內(nèi)容的信息聚合展示頁”?。
這里我們使用四色原型建模法進行建模。上面的這句話最骨干的內(nèi)容為:詳情頁是一個“信息聚合展示頁”(瞬間事件)。
圖9: 詳情頁是一個信息聚合展示頁
骨干內(nèi)容定義好后,為了更好的描述詳情頁是什么,需要補充一些實體對象,詳情頁主要包含商品、賣家、買家、魚塘這些實體(人-物-地點)。
圖10: 詳情頁中包含的主要實體
在此基礎(chǔ)上,進一步的進行抽象,用戶實體中,有賣家、買家這個角色存在;魚塘實體中,又有塘主、塘民角色存在(塘主也是塘民,所以塘主應(yīng)該繼承自塘民)。加入角色后的模型如圖11所示:
圖11: 詳情頁中的角色
最后,再把一些描述信息放進去
圖12: 模型中補充描述信息
有了模型的設(shè)計,再轉(zhuǎn)為類圖設(shè)計。根據(jù)模型的抽象,詳情頁是信息展示的聚合,因此它是個聚合根,包含了商品、賣家、買家、魚塘這些實體信息。 商品信息描述里,又有視頻、圖片、文本、互動等信息。視頻、圖片可以抽象為媒體信息。使用UML設(shè)計出最終的類圖,如圖13所示
圖13: 詳情頁類圖結(jié)構(gòu)
2.2.2 DDD代碼展示
在實現(xiàn)詳情頁時,依據(jù)的是DDD中的定義。DDD中最主要的內(nèi)容包括:entity、value object、aggregate、repository、factory和service (如圖8所示), 以及Infrastructure, Domain, application和User Interface 分層結(jié)構(gòu),如圖14所示:
圖14 領(lǐng)域驅(qū)動設(shè)計分層架構(gòu)
Infrastructure主要用于持久化數(shù)據(jù)的讀取和寫入;Domain為領(lǐng)域?qū)?#xff0c;提供領(lǐng)域信息,這是業(yè)務(wù)的核心所在;Application是很薄的一層,沒有業(yè)務(wù)邏輯,用來協(xié)調(diào)應(yīng)用活動;User Interface負責(zé)用戶信息展示。將這個分層結(jié)構(gòu)映射到工程結(jié)構(gòu)如圖15所示。
圖15:DDD領(lǐng)域建模工程結(jié)構(gòu)
這里的Applicaiton層沒有業(yè)務(wù)邏輯,只作為二方服務(wù)對外提供。此外,工程結(jié)構(gòu)中沒有寫User Interface層,因為該應(yīng)用是以二方服務(wù)提供,當(dāng)然,如果提供REST服務(wù),則可以寫在這一層。
多數(shù)的用戶對MVC架構(gòu)比較了解,因此,圖16對比了DDD的分層架構(gòu)和MVC的架構(gòu),以做參考。
圖16:DDD分層對比MVC分層
根據(jù)上文的模型抽象,領(lǐng)域?qū)ο笾饕?ItemEntity, SellerEnity, BuyerEntity, FishPoolEntity,并通過詳情頁聚合根DetailAggregate聚合。
圖17: 詳情頁聚合根
在圖15應(yīng)用結(jié)構(gòu)分層中,有3種類型的數(shù)據(jù)對象,DO對象表示持久化的數(shù)據(jù)對象,?Entity為領(lǐng)域?qū)ο?#xff0c;DTO為對外的傳輸對象。
首先通過領(lǐng)域?qū)拥腞epository,調(diào)用基礎(chǔ)設(shè)置層的Dao讀取DO結(jié)構(gòu),再使用Convertor轉(zhuǎn)為Entity領(lǐng)域?qū)ο蟆?/p>
圖18: Repository的定義
圖19: Converor轉(zhuǎn)化器的定義
領(lǐng)域entity處理各自的領(lǐng)域業(yè)務(wù)邏輯,然后通過領(lǐng)域?qū)拥腄etailService,對聚合根DetailAggregate進行整體詳情頁業(yè)務(wù)領(lǐng)域處理。最后轉(zhuǎn)為DTO傳輸對象提供對外服務(wù)。
三: 總結(jié)和思考
本文從詳情頁業(yè)務(wù)出發(fā),當(dāng)業(yè)務(wù)越來越復(fù)雜時,如何做業(yè)務(wù)的隔離,做開發(fā)人員的隔離,以及如何通過領(lǐng)域建模,形成統(tǒng)一認知。給大家提供一個可行的參考。
但沒有任何一種架構(gòu)可以適用于所有的場景,也沒有任何一個架構(gòu)是最優(yōu)的,所謂架構(gòu),都在解決“邊界”的問題。因此都需要從實際的業(yè)務(wù)場景出發(fā),明確出問題的邊界在哪里,要達到什么樣的目標(biāo),再遵循一些基本的原則和方法,基本都能夠設(shè)計出符合自己業(yè)務(wù)特性的架構(gòu)。
接下來將會給大家分享一篇從不同的視角出發(fā),進行的業(yè)務(wù)架構(gòu)設(shè)計。
?
原文鏈接
本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。
總結(jié)
以上是生活随笔為你收集整理的架构的“一小步”,业务的一大步的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在 Ali Kubernetes 系统中
- 下一篇: 阿里云发布多款云管工具,任何角色都可以轻