在一般的公司專屬應用程式中,持久化資料的介質首選就是關聯式資料庫,第一,因為關聯式資料庫比較成熟,一般開發人員都能很容易的使用。第二,可能還有一些其他的非技術原因。
記得在剛開始我學編程的時候,都是先做UI,再邊寫邏輯邊建資料表。後來基本上就是先把資料表建好,然後在寫邏輯和介面。現在就開始了新的方式,先把領域模型和領域邏輯寫好,再建資料表。然後再把他們的之間的映射層寫好。
領域模型是物件導向的,裡面有繼承,組合等。關聯式資料庫是面向關係的,裡面只有二維表和他們之間通過索引值建立的關係。二者天生就是不匹配的。怎麼把這兩者很好的匹配起來,這本身就是一個很大而且很值得研究的問題。為了能讓這兩個天然不匹配的對象關聯起來,最好的方式就是在中間架橋,這樣對象和關係表在實現時能夠盡量的排除對方的幹擾,以符合其本身最好的實現方式去實現。雖然兩者存在天生的不匹配關係,但是兩個在邏輯上的大致概念都應該是對應著的。
怎麼建資料表,我們就不說了。作為程式員要把這個對象關係表映射層寫出來。在編寫這些代碼時,肯定要照顧到兩方面。第一,寫代碼時應該盡量一個DAL類對應一個資料表。第二,有部分大粒度的對象要包含這些小粒度的DAL對象,和領域對象對應起來。總之,我感覺,在檔案組織上,以表為單元,但內部有一些之間的相互調用。但裡面傳入傳出的都是領域對象。我們寫的對象關係表映射層,即要保證程式碼群組織的清晰,有要能保證在擷取領域對象的‘傻瓜性’。
怎麼具體的寫DAL呢?(其實不應該說怎麼寫,只能說我認為應該怎麼寫。)都是包含哪些內容呢?
1.每個表的基本的增刪改查,一般都要包含。只是一般,很多情況下並不需要那麼多,有的配置資訊,可能只需要查看,有的資料量較少的資訊,例如系統中包含的角色,可能就不需要分頁查詢。這就要視情況而定。
2.留一些在同一個工作階段狀態的資料庫操作方法,例如在Nhibrnate中,ISession就代表一個會話過程。為了保證其他方法調用該方法,並且當出現錯誤時能夠執行復原操作,一些方法可能接受含有Isession的方法列表。
3.一些資料庫驗證。例如在添加使用者時,使用者名稱不可為空等,這些驗證應該放在哪兒?定義這些方法肯定是在領域邏輯層中定義,但在添加使用者時,驗證該規則是在哪兒調用呢?我曾經在UI層調用判斷過,在邏輯層調用過。不過最終我還是覺得在DAL層,在實現實現添加使用者方法的代碼中執行添加使用者代碼之前進行調用。這樣能夠保證在添加之前的一瞬間驗證資訊,並在一個會話中,保證了效能。另外在添加使用者的時候,在執行這兩個方法的時候,使用資料表鎖,鎖定整個表。不讓其他資料添加,等該使用者添加完畢後,再把鎖解開。
我覺得這種方式是最穩定的,即使在大量使用者同時添加使用者時也能保證不會出現差錯。這種方式也符合物件導向的方式,上層代碼只關心如何調用這個方法,至於驗證,上層方法就不管了,對上層方法來看,只關心執行的動作,不關心其執行時驗證的邏輯。
順便說一下,我自己分的系統驗證主要包含兩個方便,第一是領域模型本身可以做的驗證,第二就是資料庫才能做的驗證。實現的地方肯能也很清楚了。前一個在領域模型自己本身實現,例如,一個使用者的角色不可為空,使用者名稱必須大於6個字元。密碼必須10位以上,這些邏輯都是領域模型本身自己可以處理的邏輯。還有一種邏輯就是資料庫可以驗證的邏輯(這些邏輯本身都屬於領域邏輯,只是這些處理領域對象處理不了),例如系統中不能出現同名的使用者等。
在添加使用者時,即在DAL中使用者的添加方法中,應該先調用領域模型本身的驗證方法,然後初始化會話對象,然後在會話對象中調用中資料庫驗證方法,然後再調用添加到資料庫的方法。
資料庫映射層,是系統中一個很重要的地方,是系統資料持久化的入口。如果想保證資料庫中的資料的正確定,完整性,就要把這個層設計的穩定。本身持久化到資料庫中的資料都已經不正確了,那就別談系統邏輯層的穩定和正確性了。資料的正確性是整個系統的正確定和穩定性的基礎。
資料層在提供給上層代碼使用時,要保證完整性,就像添加使用者一樣,DAL層暴露給外面一個AddUser(User pUser),那客戶代碼在調用的時候,不需要判斷使用者合不合法,不用判斷當前資料庫中是否已經包含了該名字的使用者。直接調用,如果不合法,就拋出響應的異常。
很多有經驗的老程式員可能看了上面的文字覺得很有意思。可能他們一直是這麼做的(只是可能),除了這麼做比較好外,還有什麼別的更好的方案。是的,可是作為我,一個基本上自學的程式員來講(好像大部分程式員都是自學‘成才’的),我走了很多的彎路,很多現在剛開始編程的程式員犯得錯誤和走的彎路基本上應該我應該都走過的。所以我覺得對於初級程式員來說,這個地方還是很重要的。因為在剛開始時,我根本意識不到這是個問題。