本項目是基于小編的開發經驗與心得,分享小編關于領域模型的理解, 個人愚見僅供參考,希望能為渴望進步的你提供幫助。如果你感到有用對你有幫助,請不要吝嗇你的關注,求關注,求轉發。
文章有三個議題,什么是領域模型,為什么需要領域模型設計,以及領域驅動的項目結構是什么樣的?
一、領域驅動模型是什么、領域驅動模型是什么?
一、
一、領域驅動模型是什么?
如果你是第一次聽到這個詞,嗯,多么恐怖的一件事情呀! 什么是領域模型,一種新的技術嗎? 領域模型到底有什么用呢?
為什么那么多大佬都在講領域模型。網絡上充斥著著各種高端的解釋,各種高大上的名字,各種復雜的系統設計圖。
fuck !身邊總是有這樣一群人的出現??傁矚g中文里夾雜英文,英文中夾雜著中文,仿佛這樣能使他們更加自信一樣。把你講懵了,他就自信了。 very fuck !
身為技術人,盡量想把一種事情給講清楚,說明白。而不是用各種抽象的晦澀難懂但看上去高大上的名詞給解釋。千萬不要怕,下面我們通過先做一點小小的鋪墊。最后在總結領域模型的理解。
1. 貧血模型
1. 貧血模型
1. 貧血模型
在講清楚領域模型之前我們先來看引入一個詞匯 “貧血模型” ,讀到這里不要怕。只是一個詞匯而已。是對我們平時的項目代碼結構的一個形容詞。相信無論面前的你
是一個大牛,還是一個剛入行的小菜鳥。你都一定寫過這樣的代碼:
- dao層: 負責持久化
- model層: 數據模型
- service層: 服務層
- web層: 提供對UI層的訪問
嗯。這就是一個典型的貧血模型, 哇,真的好形象,這是誰想出來的詞匯,真想給他說一句 fuck you! 但是,但是,你還有更好的詞匯來形容這種項目結構嗎?
所謂貧血模型是指使用的領域對象中只有 `setter` 和 `getter` 方法(POJO),所有的業務邏輯都不包含在領域對象中而是放在業務邏輯層。
往往我們入行的初期我們都是在這樣的項目結構中進行編程的,那個時候我們的業務往往都是簡單的,對于那個時候的我們來說,這樣的代碼結構真是太好用了。清晰易懂。甚至想說一聲 i love code !!!
這個時期,我們的關注點往往不是業務的復雜度,而是技術的使用,語法的使用。以及代碼是否能編譯通過。所以下面我們來總結一下貧血模型的優點。
2. 貧血模型優點
1. 被許多程序員所掌握,對于剛入行的同學來說,這種模型很自然很舒服,典型的MVC結構
2. 它非常簡單,對于并不復雜的業務,它工作得很好,開發起來非常迅速。它似乎也不需要對領域的充分了解,只要給出要實現功能的每一個步驟,就能實現它。
3. 事務邊界相當清楚,一般來說service的每個方法都可以看成一個事務。
3. 貧血模型缺點
隨著發際線推移,隨著歷史的變遷,隨著候鳥的遷徙。不知不覺我們的業務越來越復雜了。萬惡的資本家,總想讓我們一夜之間開發一個淘寶,一夜之間開發一個百度,一夜之間開發一個QQ。于是我們的service層,不斷的
不斷的增加。代碼量從100行,200行,300行,10000行剎不住車了。終于小張忍不住了,辭職走了。留下了孤獨的你獨自承受這憂傷。
這樣代碼是什么意思? 這樣代碼能不能刪?這行代碼怎么沒有走?這樣代碼能不能拆出去? 這樣改萬一項目上線崩潰了怎么辦? 想一想老婆,望一望孩子。哎,算了吧。于是乎service復雜度指數般的遞增。這就是貧血模型的缺點。
缺點
- 所有的業務都在service中處理,當業越來越復雜時,service會變得越來越龐大,最終難以理解和維護,輕則項目組解散,重則妻離子散。
- 將所有的業務放在無狀態的service中實際上是一個過程化的設計,這與面向對象的編程風格,相向而行。(你轉身離開分手說不出來,海鳥跟魚相愛只是一場意外)
- 項目代碼寫的不少,重用的不多。(fuck and fuck = double kill)
4. 充血模
4. 充血模型
前面說我說了貧血模型,這里順便提一下充血模型,也不要怕,也只是一個嚇人的詞匯。前面我們理解了貧血模型,那么充血模型,很容易就能理解。
前面我們說貧血模型實體類只有SET GET方法,邏輯基本在服務層實現。而充血模型它的實體類里不但有狀態,還有行為,即屬性和方法都有。它的Service層很薄。顯然這不符合MVC的思想,因為充血模型中model中不僅有數據,還有狀態。維護起來非常麻煩。
5. 領域驅動總結
針對貧血模型的service層非常復雜臃腫的缺點,領域模型的概念越來越流行起來,至少在一些很多的大公司中,非常盛行。領域模型的概念不僅可以重新去設計service,同時也在微服務設計中有重要的意義。所以說領域模型其實就是要解決service越來越臃腫的一種設計思想。主要就是對service中的復雜的業務邏輯進行拆分,根據領域來進行拆分。用面向對象的思想去重新設計service。有人給他起了一個高大上的詞匯: 領域模型。
所以最后小編想用一大白話來總結一下領域模型。
領域模型就是要用面向對象的思想去重新設計充斥著復雜業務邏輯的service層。
二、為什么要進行領域模型設計?
相信看到這里的你,一定對領域模型有一個自己的認識。為什么要進行領域模型設計? 相信自己心里一定有一個自己的判斷了。貧血模型的項目結構, service層無可避免的非常的臃腫,臃腫到一個方法可能深不見底。對于業務老油條,可能還湊合能看成,
假如你是一個新的同學,當你看到這樣的代碼一定是崩潰的,假如說注釋也沒有,那你內心更是崩潰的。假如說這是一個很龐大的系統,很復雜的業務流程,這就更不用說了。
如果讀到這里,你還是對領域驅動設計感到迷茫,那么就其實這個標題也可以這樣講: 我們如何對臃腫的service進行面向對象的設計。設計的過程就是對service層的代碼進行領域設計。
而我們之所以這樣做的目的。
- 為了快樂的coding
- 為了業務系統的穩定
- 為了業務更快的迭代升級。
當然這一切的前提是你對業務有一個全局的認識,有一個前瞻性的判斷,否則也設計不出來,真正適合自身系統的領域驅動模型。
三、領域驅動的項目結構是什么樣的?
一千個人眼里有一千個哈姆雷特,沒有最好的項目結構,只有最適合自己的業務系統。本文只是小編對領域驅動的模塊的思考和認識。
僅供參考,希望對你有所啟示和引導。
1. 領域劃分|模塊化建造
領域劃分,小編感覺用另外一個詞形容也非常的合適,就是業務模塊化。所有能力都進行能力化抽象,形成模塊,形成領域。 當遇到新的業務邏輯,底層的數據結構和數據關系肯定也是一樣的。那么就可以像堆積木一樣,根據這些模塊快速的組裝成新的業務邏輯??焖俚膶崿F業務的迭代和升級。
關于這個問題,需要結合自己的業務系統來進行抽象和設計。而小編的能做的就是,提醒你模塊化設計,領域化設計的重要意義。
2. 項目結構
基礎層(外部調用,db操作) + 領域層(偏向領域的業務邏輯) + 業務層(對領域層的業務編排) + 外觀層(可以提供能力,可以提供視圖)。
有一個完善的領域層,可以方便快速便捷的對業務進行擴展。
領域層就是模塊化設計的積木。豐富的模塊化有助于業務擴展。
一定要控制項目的依賴情況。service只能出現領域層的依賴, 領域層只能存在dao層和第三方服務層。各個層代碼不能平行調用。
3. 編程規范
關于項目提出6個注意的點。如果把做項目比作是前線打仗,那么打仗最重要的是戰斗成員目標要一致。在目標不一致的情況下一定要進行充分討論(項目負責人要做的),說明情況互相妥協指定出統一的項目編程規范。去進行執行。一旦指定不能違背。否則項目質量不保。
項目固然重要,但是作為軟件開發工程師,首先要對代碼質量做保障。
4. 日志設計
天下沒有完美的項目,任何系統不存在bug是不可能的。想要發現bug并快速定位問題,日志系統的不能缺少的。
日志系統是非常重要的系統, 對系統的監控, 在設計日志系統中,我們需要關注的點
- 日志結構(目的是按照結構解析到日志引擎中)
如果想做日志的搜索平臺,一定要進行日志結構化設計,方便被搜索平臺的解析。如ELK日志搜索系統。 - 日志打印降級能力
在遇到大促時候,可以減少不必要的日志打印,要對日志打印做降級的設計 - 異步輸入日志
- 日志歸檔
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 系統日志打印 -->
<Appender name="logfile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${logger.logback.logpath}mbp-game-service.log</File>
<encoder>
<Pattern>[%date] [%-5level] %c{40} %line --%mdc{client} [%X{TRACE_LOG_ID}] [%X{dstTraceId}] %msg%n</Pattern>
<charset>UTF-8</charset>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${logger.logback.logpath}mbp-game-service.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>30</maxHistory>
<TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>512MB</maxFileSize>
</TimeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>
<!-- 異步輸出 -->
<appender name="asyncAppender" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丟失日志.默認的,如果隊列的80%已滿,則會丟棄TRACT、DEBUG、INFO級別的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默認的隊列的深度,該值會影響性能.默認值為256 -->
<queueSize>1024</queueSize>
<!-- 添加附加的appender,最多只能添加一個 -->
<appender-ref ref="logfile"/>
</appender>
<!-- 外部jar包 日志級別設置 -->
<logger level="${logger.outside.logLevel}" name="com.ibatis"/>
<logger level="${logger.outside.logLevel}" name="org.springframework"/>
<logger level="${logger.outside.logLevel}" name="JAVA.sql"/>
<logger level="${logger.outside.logLevel}" name="org.Apache"/>
<logger level="${logger.outside.logLevel}" name="com.alibaba.dubbo"/>
<logger level="${logger.outside.logLevel}" name="org.I0Itec"/>
<logger level="${logger.outside.logLevel}" name="org.dozer"/>
<logger level="${logger.outside.logLevel}" name="kafka.producer.SyncProducer"/>
<logger level="${logger.kafka.outside.logLevel}" name="org.apache.kafka"/>
<logger level="${logger.kafka.outside.logLevel}" name="org.springframework.kafka"/>
<!-- 輸出到文件,可定義更多的 Appender -->
<root level="${logger.logLevel}">
<appender-ref ref="asyncAppender"/>
</root>
</configuration>






