領(lǐng)域驅(qū)動設(shè)計

Domain-Driven Design(領(lǐng)域驅(qū)動設(shè)計,或 DDD)

基礎(chǔ)概念
Domain Primitive
是一個在特定領(lǐng)域里,擁有精準定義的、可自我驗證的、擁有行為的 Value Object 公条。
1、DP 是一個傳統(tǒng)意義上的 Value Object迂曲,擁有 Immutable 的特性
2靶橱、DP 是一個完整的概念整體,擁有精準定義
3、DP 使用業(yè)務(wù)域中的原生語言
4关霸、DP 可以是業(yè)務(wù)域的最小組成部分传黄、也可以構(gòu)建復雜組合

分析維度:接口的清晰度(可閱讀性)、數(shù)據(jù)驗證和錯誤處理队寇、業(yè)務(wù)邏輯代碼的清晰度膘掰、和可測試性。

使用 Domain Primitive 的三原則
1佳遣、Make Implicit Concepts Explicit 將隱性的概念顯性化
2识埋、Make Implicit Context Explicit 將 隱性的 上下文 顯性化
3、Encapsulate Multi-Object Behavior 封裝 多對象 行為

什么情況下應(yīng)該用 Domain Primitive
1零渐、有格式限制的 String:比如 Name窒舟,PhoneNumber,OrderNumber诵盼,ZipCode惠豺, Address 等。
2风宁、有限制的 Integer:比如 OrderId(>0)洁墙,Percentage(0-100%),Quantity(>=0) 等戒财。
3热监、可枚舉的 int :比如 Status(一般不用 Enum 因為反序列化問題)。
4饮寞、Double 或 BigDecimal:一般用到的 Double 或 BigDecimal 都是有業(yè)務(wù)含義的孝扛,比如 T emperature、Money骂际、Amount、ExchangeRate冈欢、Rating 等歉铝。 5、復雜的數(shù)據(jù)結(jié)構(gòu):比如 Map<String, List<Integer>> 等凑耻,盡量能把 Map 的所有操作 包裝掉太示,僅暴露必要行為。

實戰(zhàn) - 老應(yīng)用重構(gòu)的流程
第一步 - 創(chuàng)建 Domain Primitive香浩,收集所有 DP 行為
第二步 - 替換數(shù)據(jù)校驗和無狀態(tài)邏輯
第三步 - 創(chuàng)建新接口
第四步 - 修改外部調(diào)用

在做架構(gòu)設(shè)計時类缤,一個好的架構(gòu)應(yīng)該需要實現(xiàn)以下幾個目標:
1、獨立于框架:架構(gòu)不應(yīng)該依賴某個外部的庫或框架邻吭,不應(yīng)該被框架的結(jié)構(gòu)所束縛餐弱。
2、獨立于 UI:前臺展示的樣式可能會隨時發(fā)生變化(今天可能是網(wǎng)頁、明天可能變成 console膏蚓、后天是獨立 app)瓢谢,但是底層架構(gòu)不應(yīng)該隨之而變化。
3驮瞧、獨立于底層數(shù)據(jù)源:無論今天你用 MySQL氓扛、Oracle 還是MongoDB、CouchDB论笔,甚至使用文件系統(tǒng)采郎,軟件架構(gòu)不應(yīng)該因為不同的底層數(shù)據(jù)儲存方式而產(chǎn)生巨大改變。
4狂魔、獨立于外部依賴:無論外部依賴如何變更蒜埋、升級,業(yè)務(wù)的核心邏輯不應(yīng)該隨之而大幅變 化毅臊。
5理茎、可測試:無論外部依賴了什么數(shù)據(jù)庫、硬件管嬉、UI 或者服務(wù)皂林,業(yè)務(wù)的邏輯應(yīng)該都能夠快 速被驗證正確性。

事務(wù)腳本類的代碼很難維護因為以下幾點:
問題 1-可維護性能差
可維護性 = 當依賴變化時蚯撩,有多少代碼需要隨之改變
1础倍、數(shù)據(jù)結(jié)構(gòu)的不穩(wěn)定性:AccountDO 類是一個純數(shù)據(jù)結(jié)構(gòu),映射了數(shù)據(jù)庫中的一個表胎挎。 這里的問題是數(shù)據(jù)庫的表結(jié)構(gòu)和設(shè)計是應(yīng)用的外部依賴沟启,長遠來看都有可能會改變,比 如數(shù)據(jù)庫要做 Sharding犹菇,或者換一個表設(shè)計德迹,或者改變字段名。
2揭芍、依賴庫的升級:AccountMapper 依賴 MyBatis 的實現(xiàn)胳搞,如果 MyBatis 未來升級版本, 可能會造成用法的不同(可以參考 iBatis升級到基于注解的MyBatis 的遷移成本)称杨。同 樣的肌毅,如果未來換一個 ORM 體系,遷移成本也是巨大的姑原。
3悬而、第三方服務(wù)依賴的不確定性:第三方服務(wù),比如Yahoo的匯率服務(wù)未來很有可能會有變化:輕則API簽名變化锭汛,重則服務(wù)不可用需要尋找其他可替代的服務(wù)笨奠。在這些情況下改造和遷移成本都是巨大的袭蝗。同時,外部依賴的兜底艰躺、限流呻袭、熔斷等方案都需要隨之改 變。
4腺兴、第三方服務(wù) API 的接口變化:YahooForexService.getExchangeRate 返回的結(jié)果是小數(shù) 點還是百分比左电?入?yún)⑹牵╯ource, target)還是(target, source)?誰能保證未來接口 不會改變页响?如果改變了篓足,核心的金額計算邏輯必須跟著改,否則會造成資損闰蚕。
5栈拖、中間件更換:今天我們用 Kafka 發(fā)消息,明天如果要上阿里云用 RocketMQ 該怎么辦没陡? 后天如果消息的序列化方式從 String 改為 Binary 該怎么辦涩哟?如果需要消息分片該怎么 改?

問題 2-可拓展性差
可擴展性 = 做新需求或改邏輯時盼玄,需要新增/修改多少代碼
1贴彼、數(shù)據(jù)來源被固定、數(shù)據(jù)格式不兼容
2埃儿、業(yè)務(wù)邏輯無法復用
3器仗、邏輯和數(shù)據(jù)存儲的相互依賴

問題 3-可測試性能差
可測試性 = 運行每個測試用例所花費的時間 * 每個需求所需要增加的測試用例數(shù)量
1、設(shè)施搭建困難:當代碼中強依賴了數(shù)據(jù)庫童番、第三方服務(wù)精钮、中間件等外部依賴之后,想要 完整跑通一個測試用例需要確保所有依賴都能跑起來剃斧,這個在項目早期是及其困難的轨香。 在項目后期也會由于各種系統(tǒng)的不穩(wěn)定性而導致測試無法通過。
2幼东、運行耗時長:大多數(shù)的外部依賴調(diào)用都是 I/O 密集型臂容,如跨網(wǎng)絡(luò)調(diào)用、磁盤調(diào)用等筋粗,而 這種 I/O 調(diào)用在測試時需要耗時很久策橘。另一個經(jīng)常依賴的是笨重的框架如 Spring炸渡,啟 動 Spring 容器通常需要很久娜亿。當一個測試用例需要花超過 10 秒鐘才能跑通時,絕大部 分開發(fā)都不會很頻繁的測試蚌堵。
3买决、耦合度高:假如一段腳本中有 A沛婴、B、C 三個子步驟督赤,而每個步驟有 N 個可能的狀態(tài)嘁灯,當多個子步驟耦合度高時,為了完整覆蓋所有用例躲舌,最多需要有 N * N * N 個測試用 例丑婿。當耦合的子步驟越多時,需要的測試用例呈指數(shù)級增長没卸。

防腐層(ACL)Anti-Corruption Layer
1羹奉、適配器:很多時候外部依賴的數(shù)據(jù)、接口和協(xié)議并不符合內(nèi)部規(guī)范约计,通過適配器模式诀拭, 可以將數(shù)據(jù)轉(zhuǎn)化邏輯封裝到 ACL 內(nèi)部,降低對業(yè)務(wù)代碼的侵入煤蚌。 2耕挨、緩存:對于頻繁調(diào)用且數(shù)據(jù)變更不頻繁的外部依賴,通過在ACL里嵌入緩存邏輯尉桩,能夠有效的降低對于外部依賴的請求壓力筒占。同時,很多時候緩存邏輯是寫在業(yè)務(wù)代碼里的魄健, 通過將緩存邏輯嵌入 ACL赋铝,能夠降低業(yè)務(wù)代碼的復雜度。
3沽瘦、兜底:如果外部依賴的穩(wěn)定性較差革骨,一個能夠有效提升我們系統(tǒng)穩(wěn)定性的策略是通過ACL起到兜底的作用,比如當外部依賴出問題后析恋,返回最近一次成功的緩存或業(yè)務(wù)兜 底數(shù)據(jù)良哲。這種兜底邏輯一般都比較復雜,如果散落在核心業(yè)務(wù)代碼中會很難維護助隧,通過 集中在 ACL 中筑凫,更加容易被測試和修改。
4并村、易于測試:類似于之前的 Repository巍实,ACL 的接口類能夠很容易的實現(xiàn) Mock 或 Stub,以便于單元測試哩牍。
5棚潦、功能開關(guān):有些時候我們希望能在某些場景下開放或關(guān)閉某個接口的功能,或者讓某個接口返回一個特定的值膝昆,我們可以在ACL配置功能開關(guān)來實現(xiàn)丸边,而不會對真實業(yè)務(wù)代 碼造成影響叠必。同時,使用功能開關(guān)也能讓我們?nèi)菀椎膶崿F(xiàn) Monkey 測試妹窖,而不需要真正物理性的關(guān)閉外部依賴纬朝。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市骄呼,隨后出現(xiàn)的幾起案子共苛,更是在濱河造成了極大的恐慌,老刑警劉巖蜓萄,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件俄讹,死亡現(xiàn)場離奇詭異,居然都是意外死亡绕德,警方通過查閱死者的電腦和手機患膛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耻蛇,“玉大人踪蹬,你說我怎么就攤上這事〕伎В” “怎么了跃捣?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長夺蛇。 經(jīng)常有香客問我疚漆,道長,這世上最難降的妖魔是什么刁赦? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任娶聘,我火速辦了婚禮,結(jié)果婚禮上甚脉,老公的妹妹穿的比我還像新娘丸升。我一直安慰自己,他們只是感情好牺氨,可當我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布狡耻。 她就那樣靜靜地躺著,像睡著了一般猴凹。 火紅的嫁衣襯著肌膚如雪夷狰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天沼头,我揣著相機與錄音瘫证,去河邊找鬼。 笑死庄撮,一個胖子當著我的面吹牛背捌,可吹牛的內(nèi)容都是我干的毡庆。 我是一名探鬼主播么抗,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼徘溢,長吁一口氣:“原來是場噩夢啊……” “哼站粟!你這毒婦竟也來了奴烙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蹦渣,失蹤者是張志新(化名)和其女友劉穎认臊,沒想到半個月后剧腻,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體书在,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡栈源,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片垃杖。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡旺垒,死狀恐怖骇钦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤棍好,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布业稼,位于F島的核電站,受9級特大地震影響掂墓,放射性物質(zhì)發(fā)生泄漏谦纱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吃嘿。 院中可真熱鬧祠乃,春花似錦嘱支、人聲如沸扔枫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至讶踪,卻和暖如春柱查,著一層夾襖步出監(jiān)牢的瞬間廓俭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工唉工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留研乒,地道東北人。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓淋硝,卻偏偏與公主長得像雹熬,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子谣膳,可洞房花燭夜當晚...
    茶點故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內(nèi)容