為什么要讓我們的“領域模型”裸奔?

作者: 菜阿彬  來源: 博客園  發布時間: 2011-05-11 06:41  閱讀: 1428 次  推薦: 0   原文鏈接   [收藏]  

  為什么要讓我們的“領域模型”裸奔?(上)

  做不完的應用軟件

  我爸是個鄉村小學教師,對我所從事的軟件行業一無所知,但是他對我的工作穩定性表示懷疑:“你這做軟件的,要是有一天軟件做完了,你豈不是要失業了?”也許他想起了他作為老師的情況,教完一批學生,下一批又上來了,一茬一茬的。于是又問我:“你們是不是一個軟件接著一個軟件做?”我回答他:“不是,就一個軟件,好幾十個人得做好幾年呢。”解釋了很多次仍舊沒有消除他的疑問:“你們做軟件怎么會一直做下去?怎么沒有個做完的時候呢?”。

  如果他在通往張江的地鐵上,知道有那么多我傷不起的IT同類們,他也許會更加迷惑。為什么如此龐大的程序員大軍,日復一日年復一年地敲著代碼,生產出無數的軟件,可是他們不用擔心失業?為什么需要那么多看上去類似的軟件?為什么這些軟件永遠沒有做完的那天?

  應用軟件獨一無二的地方

  答案其實很簡單,我們做的每一套軟件,都是為了解決某個領域的業務需求。而業務需求永遠沒有停止變化的一天,這就是為什么應用軟件永遠也做不完的原因。我剛開始工作時,在一個小團隊里為一家公司定制開發ERP,其實就是一個中小型的管理系統,在知道國內有用友金蝶在做ERP的時候,覺得很奇怪:有那么好的公司在做ERP,我們還有做的必要嗎?客戶怎么不去買用友金蝶呢?現在想來這想法很幼稚,因為每家公司的業務都是獨一無二的,因此每個應用軟件,即使都叫ERP,它也是獨一無二的。 

  把獨一無二的東西分離出去

  想想我們為了構建一個應用軟件起來,要做哪些事?最基本的三件是:Domain Business Logic、Presentation(UI展現)、Persistence(存數據庫)。還有就是Authentication,Authorization,Cache等等。復雜的系統還包括:與其它系統的集成,提供Service(API)等等。

  就說最基本的3件吧,稍微思考一下就會發現,只有Business Logic是獨一無二的

  Presentation層是個重復重復再重復的事情,再怎么不同的應用,我們都可以用同一套工具來實現它:用Grid來展現多條記錄;用Combobox來提供選擇;用MVC模式來分離數據與展現……框架應運而生。

  Persistence也是個重復重復又重復的事情,關系型數據庫,ORM框架,NoSql……同樣約定俗成。

  所有的東西都可以找到框架,這就是為什么寫應用軟件,跟寫游戲,或者寫操作系統等比起來,是最沒有技術含量的。

  然而唯獨“業務邏輯”沒有框架。正是這“業務邏輯”,讓每個應用軟件區別于其它應用軟件。因此我們決定要做一個應用軟件,我們要做的就是實現客戶的業務邏輯,這是唯一真正的目的。持久化,UI,Cache……,都是手段。

  如何實現業務邏輯?

  現在知道了,我們做的每一個系統,都在為客戶交付獨一無二的業務價值。這就解決了程序員們“存在的意義”的哲學問題。那么如何實現業務邏輯呢?把客戶的業務需求轉化為解決方案的過程,就是設計的過程。因此,這個問題可以這樣問:通過何種的設計,讓客戶的業務需求得到滿足呢?這個問題很傻,因為顯而易見是通過寫代碼實現啊,這不是常識嗎?很可惜這不是常識,因為很多團隊把設計寫在設計文檔里了。他們把寫文檔說成設計,把寫代碼說成實現。大錯特錯,代碼是唯一的設計,MsBuild把代碼build成exe才是實現!用Unit Test確保自己的設計(即代碼)正確反應了自己腦中的設計意圖,用Integration Test來確保自己的設計(即代碼)正確地滿足客戶的需求,有著這種認識和追求的程序員,是我想要共事的程序員。

  想起來,臺灣習慣把程序員說成“設計師”,是更貼切的。把代碼當做設計的程序員是幸福的,比較一下大樓的設計人員,當他們設計好的方案(設計圖紙)一旦被建筑工人們開始“實現”的時候,他們的設計幾乎就不能再改了,因為“實現”的成本太昂貴了。而作為軟件設計師,我們的“建筑工人”MsBuild包工頭以及它的團隊(CPU,RAM等小兵)是多么的廉價和高效,幾秒鐘就把我們的設計給實現了。這就是我們能夠利用“重構”技術的理由。(想象一下如果大樓設計人員也這么說:“你們先按照這方案蓋起來,我看效果,然后再調整(重構)”……)

  與傳統行業的設計師相比,我們軟件設計師能得到的反饋更快更多(因為我們面對的是電腦),這就是我們幸福的地方,也是我們應該利用的地方。準備寫個“軟件開發中的反饋系統”系列文章闡述此問題。

  在哪里實現業務邏輯?

  用代碼實現業務邏輯,那么,在代碼的哪個地方呢?有很多個地方:

  1,前些年很常見如今被人很鄙視的一種是,存儲過程。這種曾經非常流行的技術,自然有它產生的原因。存儲過程是什么?是數據庫里的東西,而且是關系型數據庫里的東西。很多人的思維是這樣的:當他試圖理解一個業務邏輯時,他心里想的是表以及表與表之間的關系,這就是Database-Driven邏輯,在這種邏輯下,把業務邏輯寫在存儲過程里,是很自然的事情。如果把思維切換到Domain-Driven的模式中:業務邏輯是我的核心,持久化只是一個輔助的手段,我可以用關系型數據庫,也可以用NoSql,而NoSql根本沒有存儲過程,如此,你把業務邏輯寫在存儲過程中讓人情何以堪啊?

  2,寫在UI里,這就要提到當年的RAD之王Delphi了。并不是說在Delphi里只能這么做,而是說Delphi里很多人就這么做,UI直接綁定DataSet,用戶點擊了某按鈕,直接在IDE里雙擊該按鈕,生成Btn1_Click方法,把業務邏輯通通寫在那。這么做有一萬種缺點,但有一個優點,就是RAD中的R(Rapid)。哥用這種方式寫過好幾年的Delphi,不堪回首。

  3,寫在MVC的Controller里,其實等同于2。現在Asp.net MVC框架很熱,可是很多人不知道MVC本質上是啥東西。雖然它有個“M”,可是他在分層的架構體系里,只是非核心的Presentation層的一個pattern而已。跟Domain層毫無關系。

  4,最好的方式,當然是寫在一個獨立的Domain Layer里。別忘了業務邏輯是一個應用系統唯一獨一無二的地方。

  如何實現Domain Layer(Business Logic Layer)?

  我做過幾次技術面試,一般都會有個問題:“你能說說你對架構的理解嗎?”得到的回答,第一句往往是:“關于架構,一般是分成3層,Presentation,Business Logic,Persistence……”。這句話即使是很Junior的人也能說得上來,可是再往下問就能問出有意思的東西了:3層之間的依賴關系是怎樣的?

  一般的回答是:Presentation依賴于Business Logic, Business Logic依賴于Persistence。

  可是既然每個應用系統的“業務邏輯”才是應用系統存在的理由,才是開發它的目的所在。而UI展現、數據庫存儲、Cache等都是為了實現“業務邏輯”這個目的所提供的手段,都有成熟的框架、模式可用,都可以是雷同的。

  那么為什么“業務邏輯”要依賴于“存儲技術”?為什么“目的”要依賴于“手段”?

  --------------------------------------------

  為什么要讓我們的“領域模型”裸奔?(下)

  在此對上篇做下補充說明:

  1,因本人畢業以來從事的項目全是業務邏輯復雜的企業應用軟件,ERP,SCM,HRP,CRM……,這種系統,如Martin Fowler在PEAA一書中所說,是適合使用Domain Model的,上文和本篇討論的都是基于這樣的場景和前提。

  2,正如一哥們回復中說的,天下沒有絕對的東西,我們都在寫隨筆,不是寫論文。這兩篇文章只是提供一種看待問題的視角,看問題的視角多了,到了具體的項目,就會有更多的選擇。

  3,寫上篇時沒想到要分上下篇,導致整個上篇沒有說明啥叫“裸奔”,不過從評論看,大部分人都讀懂了:就是讓“領域模型”不依賴于其它任何東西(如數據訪問層)。

  天氣熱了,實在不想下了班還鼓搗技術,不過想想還是一鼓作氣寫完拉倒。

  邏輯依賴與物理依賴

  上篇留下的問題是:為什么“業務邏輯”要依賴于“存儲技術”?為什么“目的”要依賴于“手段”? 

  其實“目的”依賴于“手段”并沒有什么問題,但更準確的說法應該是“目的”受約束于“手段”,具體說就是“業務邏輯層”受約束于“數據存儲層”,舉個例子,如果使用NHibernate作為ORM框架,設計的“領域模型”一定是把所有屬性都設置為virtual,為了遷就于NHibernate的LazyLoad實現技術。這種遷就或者依賴是無法消除的,然而這里說的是概念上或邏輯上的依賴。

  如果到了具體實現上,仍然存在這種依賴,就成了物理上的依賴,簡單地說就是BLL這個assembly會對DAL這個assembly有個引用。物理依賴有什么問題?

  反饋延遲帶來的傷害

  先離題一下說說反饋。舉個例子,我們拿著杯子去飲水機接水,隨著水位的上升,我們知道何時應該停止,這就是眼睛看到水位后,大腦給出的反饋。如果反饋延遲(哪怕只延遲2秒)甚至根本沒有反饋,會有什么后果?水溢出來了,大腦才反應過來,后果一定是手被燙到。

  簡單的例子可以說明反饋被延遲帶來的危害。然而在軟件開發中,很多團隊不斷地被延遲的反饋所反復蹂躪傷害。此話怎講呢?

  舉個例子吧,“代碼即設計”,如果代碼就是我們的設計,那么如何保證我們的設計正確?很多團隊最常見的辦法是人肉測試。把代碼打包成軟件,然后丟給測試人員甚至客戶。在我經歷過的一個瀑布式軟件過程里,今天寫好的代碼,也許要一個月后才會到測試人員手中,半年后到客戶手中,也就是說,外界對我們設計(代碼)的驗證和反饋周期,需要幾個月之久。這是多么大的延遲,2秒延遲就會燙傷我們的手,幾個月,我們傷的起嗎?

  如何加速反饋

  這就是“迭代開發”被引入的一個理由:縮小反饋周期。一個迭代(常見的是2周)內必須把反饋圈給結束掉,也就是2周內完成一個Feature的需求分析、設計、代碼、測試等所有環節。從這個角度出發,如果一個迭代里不能getting things done,那不叫迭代,那就叫“兩周”。  

  對于一個Feature來說,兩周的反饋周期是可以接受的,畢竟每兩周有個功能點給客戶看看,確保我們do the right thing,很不錯了。

  然而如何保證我們do things right(比如,設計和可維護性等等足夠好)呢?還有,這兩周做的正確的東西,如何保證隨著功能的不斷增加而不會在將來被破壞呢(答案:回歸測試)?如果每兩周都人肉回歸以前做過的所有功能,那就需要太多QA了。

  答案就是自動化測試Unit Test保證do things right;驗收測試/集成測試來保證do right things。

  自動化測試金字塔

  

  如圖,意思是什么呢?如果一個項目的所有自動化測試用例是100,那么最下面的Unit Test應該占80個左右,中間的集成測試占15個左右,上面的UI驅動的驗收測試占5個左右。(還有個最上面的人肉測試,那是浮云:))為啥呢?因為Unit Test的ROI(投資回報率)最高,它上手容易、運行快,UI驅動的驗收測試的ROI最低,運行慢、維護成本高(因為UI是很易變的,UI一變,UI測試腳本就得改。)

  所以一個團隊如果要開始自動化測試,最好從Unit Test開始。而最應該寫Unit Test的地方是哪個地方呢?毫無疑問,是我們的“目的層”——“領域模型層”。

  Persistence Ignorance

  回到我們的問題,“領域模型層”對“數據存儲層”有物理上的依賴,導致的不好的結果就是,很難寫Unit Test。想象一下,有個Customer類,它的AddOrder()方法里面調用了DAL層的東西,也就是連接了數據庫,那我跑我的UT時也一定要連數據庫。連數據庫的UT那不叫UT。

  怎么辦呢?“依賴反轉”,Inversion Of Control,IOC。具體做法是:本來BLL依賴于DAL,現在抽一個接口IDAL,讓BLL依賴于IDAL,DAL從IDAL繼承。從Assembly上來說,BLL和IDAL放到一個Assembly里,DAL放到另一個Assembly,那么DAL這個Assembly現在對BLL那個Assembly有個依賴了。——這樣,就把依賴給反轉了。然后通過Dependency Injection,在運行時把DAL作為IDAL的運行時實例,注入到BLL中。這就是IOC和DI的關系,他們其實不是一個東西,只不過很相關,有時就用IOC或DI泛指這項技術了。

  BLL對DAL的依賴,從編譯期延遲到了運行期,編譯期對DAL沒有依賴,只對IDAL有依賴,這就是Persistence Ignorance(不知的請google之)。

  這有多重要?

  BLL(領域模型)開始裸奔了,它對其它層沒有依賴,我們可以為他寫豐富的Unit Test,這有多重要?

  每個unit test都用其方法名說明了我們的設計意圖,甚至小片業務邏輯,比如有個測試用例,方法名叫“should_promote_to_VIP_when_customer_buying_platinum_card”,如果讓你接手一個別人留下的代碼,你不是很清楚里面的業務邏輯,你是愿意去看文檔?還是愿意去看他留下的存儲過程、或者100行又臭又長的方法?還是愿意看這樣的一句話:“當客戶買了白金卡后,應該把它提升為VIP”?

  unit test的覆蓋率足夠高時,我們讀完所有的unit test方法名(只是名字),我們已經了解了大部分的業務邏輯。

  事實上,一個項目的維護成本往往是開發成本的四五倍甚至幾十倍(越差的代碼,這個比例越高)。另外大家也深有體會:讀代碼比寫代碼難。那么為了降低讀代碼或者維護別人/自己代碼的痛苦(當老板的,降低維護成本意味著白花花的銀子啊),有啥理由不讓我們的“領域模型”裸奔呢?

  豐滿的領域模型裸奔著向我們呼嘯而來

  下圖是敏捷宣言簽署者之一的Alistair Cockburn的Hexagonal Architecture,很精彩的圖,留作參考資料,大家意會,不解釋了。

0
0
 
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()