前面寫了這麼多,很大程度上就是為了這一章做準備。物件導向或者領域驅動,最重要的一點就是要忘記資料庫!我花了很長很長的時間,才理解了這一點,從而真正的邁向一個嶄新的天地;而後,我又花了很長很長的時間,才勉強做到這一點;我希望,有一天,這將不再是一個問題,我不需要考慮這一點……
為什麼業務層這麼薄
三層架構流行起來之後,我們很清楚的知道UI層負責頁面互動並呼叫下一層,也知道DAL層就是和資料庫打交道。但BLL層?什麼才算是“業務邏輯”?有各種各樣的解釋,但這些不都是sql做的麼?對於絕大多數的應用系統而言,除了對資料庫進行“增刪改查”以外,實在不知道還能做些什麼?更何況,不是還有超級強大的儲存過程麼!
所以,很多系統,即使勉強弄出一個業務層,也“薄”得不像話,像一層塑料薄膜一樣,讓人有一種把它立即撕掉的強烈的衝動。
為什麼會是這樣呢?
這得從.NET陣營從歷史說起。.NET陣營的同學知道三層架構,多半是從PetShop開始,這被奉為三層架構的經典,很多專案甚至是直接複製其架構。在當時,它是一種了不起的進步。那時候,還是從ASP向ASP.NET轉型的過程,很多asp專案,sql程式碼都還是寫在html裡面的!所以,UI和DAL的分離,無疑具有明細的示範效應。
但微軟的步子,不大不小,剛好扯著蛋。
步子小一點,做成兩層架構,估計一點問題都沒有,大家都能接受;步子再大一點,就得上ORM,可惜微軟當時還沒條件支援。所以就搞出了這麼個不明不白稀裡糊塗的概念出來,折磨了我好久好久……
長期以來,.NET的陣營,在應用級層面,其實是“面向資料庫”的。從DataSet、DataGrid、DataSourceBinder之類的,都可以看出來。即使是Entity Framework,最開始也是從資料庫的表向.NET的類進行對映。這些,都極大的制約了.NET陣營同學物件導向的思維擴充。
好在我終於跳出來了。
面向資料庫
為了說明,我們舉一個最簡單的例子。
需求是:記錄文章(Article)的瀏覽數(ViewCount)。每當文章被閱讀(View)一次,瀏覽數加一。
看到這個需求,你首先想到的是什麼?是不是:
1 |
Update Article set ViewCount = ViewCount + 1; |
如果是這樣的話,恭喜你,你還牢牢的守住了“面向資料庫”的陣地。
1 2 3 4 5 6 |
/* 面向資料庫並不是不可接受的,物件導向也並不一定比面向資料庫“高階”。 這只是兩條道路的選擇,如果你願意看一看另外一條路的風景,就請繼續;否則,就此打住吧。 */ |
物件導向
那麼,物件導向或者領域驅動應該是怎麼做的呢?
1 2 3 4 5 6 7 8 9 10 11 |
public void View() { //從資料庫中取出Article Article article = session.Load<Article>(articleId); //改變Article的ViewCount屬性 article.ViewCount += 1; //將改變後的Article再存入資料庫 session.Save(article); } |
有什麼感覺?眼前一亮,還是不可思議?想得更深一點的,是不是覺得這是多此一舉,一句sql就能解決的問題,搞得這麼複雜?
我當年,考慮最多的,最不能接受的,是效能問題。
- 這必須利用ORM,即使不考慮ORM生成的sql高不高效,就這生成sql的開銷,應該就不低吧?
- 這樣做,取資料,開啟一次資料庫連線;存資料,又開啟一次資料庫連線。就算有連線池,但能省一點就省一點不是更好?
所以,如果你也和我一樣,倒回去看我之前的部落格吧!
這樣做,還有其他很多具體的技術問題,我們後續部落格會逐一展開說明。
為什麼
我們還是回到大方向上來,為什麼要這麼做?換言之,“面向資料庫”有什麼問題,或者說“物件導向”有什麼好處?
我覺得,“抽象”、“解耦”、“複用”之類的說法,都還沒有觸及根本。最根本的原因,還在於我們的大腦,我們的大腦不適應於把這個世界抽象成一張一張的表,而更適應於一個一個的物件。隨著系統日趨複雜,這種現象就表現得越明顯。
我曾經參與過一個專案,它的資料庫結構列印出來,得用地圖那麼大一張紙(我不知道算A幾了),密密麻麻的全是表,各種線條交錯其中,我看著就頭皮發麻。部門裡面像個寶貝一樣把這張表供著,因為公司沒法列印也沒法影印啊!(我不知道他們最開始是怎麼得來的,估計肯定麻煩)
如果你一邊讀一邊在想,就會發現,“不對呀,有多少表就有多少類,類圖不是一樣複雜嗎?”
是的,而且由於抽象,類很可能比表還要多。但是,有於抽象,在我們進行架構、設計、溝通的時候,可以暫時的拋棄很多細節。比如,我們可以說,“文章被評論之後,文章作者的積分加10分”,這個時候,我們就不考慮文章有很多種:部落格、新聞、問答、評論……,也不考慮積分增加是直接改積分總分呢,還是新增一條積分記錄,或者還要同步……。如果只有表,你怎麼說?
當然,表的結構也可以設計成類似於繼承的樣子(類的繼承關係也最終會對映成表結構),但是,在交流溝通中,你如何表明這種抽象關係呢?
單純從程式的角度上說,使用ORM,物件導向,還增加了系統的複雜性。畢竟多了一道工作,而且把物件對映到資料庫不是一件簡單的工作,尤其是你還要考慮效能問題的時候。
那為什麼我們還要這樣做?委託,換言之,把複雜性往其他地方推。我記得我反覆講過這一點,架構的一個重要工作,就是把複雜性進行拆分和推諉。拆分估計大家好理解,但“推諉”是個什麼意思,推給誰呢?管它呢,我只做我分類的事,其他的,UI推給BLL,BLL推給DAL,DAL推給DBA,DBA推給採購部……
寫在這裡很搞笑,但事實就是這樣的。在效能篇,我說,你要寫高效能的程式碼,你就是搶了人家的飯碗,就這個意思。UI都把DBA的活兒幹了,人家吃什麼?你程式碼都寫成01001010101010二進位制了,別說做彙編的,估計做CPU的都活不下去了。
我們這裡,就是把複雜度推給了Map團隊、ORM工具開發商和DBA。
因為我們要和客戶談需求啊,典型的是領域驅動,要和客戶/領域專家找到“共同的語言”,這共同的語言是什麼?是表結構?估計如果開發的是一個財務會計系統,這還是可行的——估計早期的系統大多就是財務報表類系統?說不定還真是這樣。為什麼物件導向從Java開始流行,Java是虛擬機器,可以用在微波爐報警器之類上面的,底層資料結構可以完全脫離資料庫啊!.NET做什麼起家的,就報表啊!呵呵。
總之,發展到今天,隨著系統複雜性的增加。在系統的架構設計中,我們不得不將現實世界首先對映成一個一個可以封裝、具有繼承多型特性的物件,並且將重心放在這些物件關係功能的維護上。
資料庫?就先不管它吧。
只有脫離了資料庫的束縛,我們才能自由的翱翔在物件導向的世界裡!
忘不掉
“問題是我忘不掉啊!”
“我只要看到需求,腦子裡馬上就是資料庫就是表。”
“沒有資料庫,我都不知道怎麼開始寫程式碼了。”
……
是的,忘掉資料庫是很難很難——尤其是對於我們這些老人來說。已經浸淫sql數十年的高手,你讓我忘掉它?你以為寫小說啊,張無忌學太極啊?
我只能說說我是怎麼做到的,希望能給你一些參考。
我就假設我的系統不是用“關聯式資料庫”儲存資料,不是mysql,不是oracle;我用nosql,我用xml檔案儲存,行不行?nosql,怎麼用?不知道啊,我十竅通了九竅。但我就要在我還不知道nosql怎麼用的時候,就開始構建我的BLL/領域層。而且我只設定幾個最簡單的假設:
- 所有的物件都可以直接從硬碟Load()出來
- 所有的物件都可以直接Save()到硬碟
- 物件之間用1:1、1:n、n:n建立關聯即可
究竟怎麼從硬碟裡存取(所謂的“持久化”),以後再說。我連用什麼進行持久化都不知道,現在怎麼考慮?但有一條,反正不會用關聯式資料庫,估計是用nosql吧……
最終的期望
真正的物件資料庫!快出來啊,求你了……