當我們已經在應用程式中使用 ORM(例如 Entity Framework),還需要 Repository 嗎?這個問題就像「要不要使用 ORM」一樣,沒有標準答案。這類「該不該」的問題,有時真的挺傷腦筋。

更新紀錄
  • 2012-11-30:些微潤飾,增加參考資料。
  • 2014-05-08:補充一點新發現,增加延伸閱讀文章。

何謂 Repository?

按照 Martin Fowler 的定義,「Repository 是領域層與資料對應層之間的媒介,如同存在記憶體中的物件集合。用戶端會建構查詢規格(query specification),並傳遞給 Repository 以執行特定的資料操作.....概念上,Repository 將資料與其相關操作封裝成物件集合,以便用貼近物件導向的方式來存取資料」。

在設計 Repository 時,常見的做法是為每一個「主要的 entity」設計一個 Repository 類別,稱為 aggregate root。例如客戶資料會有對應的 CustomerRepository,產品資料則是 ProductRepository,訂單則是 OrderRepository;這些都是 aggregate roots。至於訂單細項,應該由 OrderRepository 一併負責處理,就不另外設計 OrderItemRepository。

此外,為了減少重複的程式碼(DRY 原則),有些人會使用泛型 Repository

如果你想看看 Repository 或者分層設計的範例,可以參考:

有了 ORM,還需要 Repository 嗎?

在 ORM--或者說 Entity Framework--還沒那麼成熟的年代,使用 Repository 模式的理由很充分,好處也明顯。可是,當 Entity Framework 和 NHibernate 已經提供了同樣、甚至更多的功能,我們還需要再多加一層 Repository  嗎?

網路上可以找到很多文章,介紹如何搭配 Entity Framework 來設計我們的 Repository。所以「怎麼做」並不是問題,問題在於我們是否該這麼做。

使用 Repository 的理由通常有這幾個:
  1. 方便單元測試。
  2. 避免上層服務直接依賴 Entity Framework,以便將來可以將 EF 換掉,換成 NHibernate 或其他 ORM。
  3. 可搭配 Unit of Work 模式來管理交易。

拿掉 Repository 之後,單元測試還是可以做,只是測試對象改成了商業邏輯層、服務層、或其他更上層的服務;單元測試的粒度可能會粗一點,視情況而定。如果連商業邏輯層都沒有,例如直接在 MVC 應用程式的 Controller 中使用 Entity Framework,單元測試也許會更麻煩一些。

其次,可抽換底層的資料存取元件當然很理想。然而,一旦使用了 Entity Framework,就算我們用 Repository 把它包起來,難保不夠周密,讓某些與 EF 相依的東西悄悄從縫隙中溜出去。除了擔心成本效益,我也懷疑真實世界中有多少案子真的需要而且實際達成了「任意切換 persistence layer」的理想。

至於 Unit of Work,其實 EF 的 ObjectContext 和 DbContext 已經提供了類似的功能。

如此說來,ORM 之上再疊一層 Repository,可能會讓我們費好一番工夫寫一堆不那麼有用、而且長得很像的程式碼,可能增加了程式的複雜性,卻得不到相對比例的好處。因此,在不需要切換 persistence layer 的情況下,比較小而單純的應用程式,我可能不會使用 Repository。如果是比較大型或複雜的系統....嗯,目前我也說不準。

誠如開頭說的,這裡沒有標準答案,有的只是個人主觀的想法(而且以後可能會變)。

文後附上一些參考文章,如果你也碰到類似問題,可以自行研究看看,寫點程式感覺一下,再做決定。

2014-05-08 補充

www.asp.net 網站上的教學文章 Getting Started with EF 5 using MVC 4 裡面有一個步驟是 Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC Application,可是到了 EF 6 和 MVC 5,新版本的文章 Getting Started with EF 6 using MVC 5 裡面卻把實作 Repository 的步驟拿掉了,如下圖。不知道是不是有什麼原因。



延伸閱讀