應用程序框架實戰二十一:DDD分層架構之倉儲(介紹篇)


  前面已經介紹過Entity Framework的工作單元和映射層超類型的封裝,從本文開始,將逐步介紹倉儲以及對查詢的擴展支持。

什么是倉儲                                                  

  倉儲表示聚合的集合。

  倉儲所表現出來的集合外觀,僅僅是一種模擬,除了測試以外,沒有理由使用內存中真正的集合來創建倉儲。

  不應該為所有實體建立倉儲,只有聚合才擁有倉儲。

  倉儲用來重建已持久化的聚合,而工廠用於新建聚合。

使用倉儲的優點                                                  

  直接使用Entity Framework的DbContext不是很好嗎,為什么還要在DbContext的上方封裝一層倉儲呢,這是否多此一舉?

  很多使用EF的程序員確實是直接使用DbContext,並且他們發現開發起來十分簡單,因為DbContext的接口設計得非常優雅,從接口上看,DbContext就好像所有實體集合的倉庫。

  另一方面,很多使用了倉儲的朋友,都是依葫蘆畫瓢,雖然創建了倉儲,但並沒有體會到多大好處。

  下面簡要介紹使用倉儲將為你帶來的優點。

從概念上簡化數據操作

  倉儲模擬了某種聚合的集合,而DbContext包含了N種類型的集合。

  與倉儲相近的一個數據訪問模式是數據訪問對象(DAO)模式,很多人認為倉儲不過是數據訪問對象換了一個名詞而已,從技術上說的確如此,倉儲的強大之處在於概念上更簡單。倉儲在概念上代表內存中的集合操作,而數據訪問對象代表數據庫操作,很明顯,集合比數據庫在概念上更簡單。

減少冗余查詢邏輯

  如果直接使用DbContext,由於特定查詢邏輯沒有一個固定位置,可能分散到任何地方,這很容易造成冗余。

  倉儲是封裝特定查詢邏輯的好地方,對於特定的查詢邏輯,放到與聚合相應的倉儲中即可。

降低耦合度

  直接使用DbContext,所有調用代碼與EF實現高度耦合。

  另一方面,由於DbContext能夠獲取任意實體,這些實體可能位於聚合內部,這樣會破壞聚合的封裝性,同時在任意位置可以獲取任意對象,由於缺乏約束力而導致更高的耦合。

方便單元測試

  直接使用DbContext只能進行集成測試,必須連接到真實數據庫。而領域層只持有倉儲接口,所以測試時很容易替換成模擬實現,從而避開數據庫。

使用倉儲的要點                                                  

使用更具體的倉儲

  一般來講,ICustomerRepository比直接使用泛型的IRepository<Customer>要好。

  為了定義通用操作,我們會創建一些泛型基類來實現基礎操作。

  有些人發現很多具體倉儲只是直接繼承泛型倉儲,比如ICustomerRepository和CustomerRepository從泛型的IRepository<Customer>和Repository<Customer>派生,但CustomerRepository本身並沒有其它什么代碼。於是很多人學會偷懶,直接使用泛型基類進行操作。

  ICustomerRepository優於IRepository<Customer>的原因如下。

  1.  概念上更清晰。

  ICustomerRepository從概念上良好表達了該倉儲用於操作客戶,而泛型倉儲可以操作任何聚合。

  2.  為特定查詢邏輯提供唯一封裝和訪問點。

  ICustomerRepository可以將客戶相關的各種復雜查詢封裝起來,而IRepository<Customer>很難對客戶查詢進行封裝,一個辦法是使用擴展方法來完成封裝,不過比起CustomerRepository要麻煩得多,而且實現代碼也更難管理。

  3.  降低耦合。

  泛型IRepository<>可以在任意位置獲取任意聚合,這比起DbContext要強一些,不會破壞聚合的封裝性,但缺乏約束力仍然會導致更高耦合。

  ICustomerRepository通過明確的聲明,只能獲取需要的聚合,從而控制了對象的訪問。

倉儲僅返回與其聚合高度相關的內容

  倉儲可以返回對應的聚合,也可以返回聚合相關的統計信息,甚至可以返回聚合的一個子集。

  有人在倉儲查詢中使用匿名對象,使用匿名對象的原因很簡單,即為了進行投影操作,這樣可以限制Sql查詢返回的列,對於Sql性能調優來講,這可能非常重要,比如使用Sql Server的覆蓋索引。

  但是使用匿名對象進行查詢有很多問題,一個問題是無法直接作為結果返回。有人為了省力,直接使用聚合或其部分子對象作為返回類型,其結果是對象中的一部分屬性被賦值,另一部分屬性為null。這可能導致許多神秘的Bug,另外導致你或其它人對倉儲查詢信心不足,因為這些查詢並不安全,你每當需要調用一個查詢,首先得仔細檢查代碼,看看哪些值是空的,以免踩到地雷。

  如果要返回聚合的一個子集,需要單獨定義一些對象,以作為返回類型。工作量雖然比較大,但更加安全和清晰。

  如果需要獲取的內容由多個聚合組成,這個查詢操作應該放入哪個倉儲中?這種情況放到哪個倉儲其實都不合適,更好的辦法是使用一個查詢服務,就好像跨越多個聚合的業務操作需要領域服務一樣。

總結                                                  

1. 倉儲的定義

  倉儲表示聚合的集合。

2. 倉儲的優點

  • 簡化數據操作
  • 減少冗余查詢邏輯
  • 降低耦合度
  • 方便單元測試

3. 倉儲的要點

  • 使用更具體的倉儲
  • 倉儲查詢僅返回與其聚合高度相關的內容 

 

 

  由於倉儲是對數據操作的封裝,包括倉儲基礎,分頁,查詢擴展,查詢對象,查詢條件(規約模式),查詢條件應用(日期范圍與數值范圍查詢)等內容,所以需要多篇文章進行介紹。 

  .Net應用程序框架交流QQ群: 386092459,歡迎有興趣的朋友加入討論。

  謝謝大家的持續關注,我的博客地址:http://www.cnblogs.com/xiadao521/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM