在DDD設計中大家都會使用Repository pattern來獲取domain model所需要的數據。
1.什么事Repository?
"A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction. Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the Repository will carry out the appropriate operations behind the scenes. Conceptually, a Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer. Repository also supports the objective of achieving a clean separation and one-way dependency between the domain and data mapping layers."
按照最初提出者的介紹,它是銜接數據映射層和域之間的一個紐帶,作用相當於一個在內存中的域對象集合。客戶端對象把查詢的一些實體進行組合,並把它 們提交給Repository。對象能夠從Repository中移除或者添加,就好比這些對象在一個Collection對象上就行數據操作,同時映射 層的代碼會對應的從數據庫中取出相應的數據。
從概念上講,Repository是把一個數據存儲區的數據給封裝成對象的集合並提供了對這些集合的操作。。。。。。。
在領域驅動設計中,我們有個集合(aggregate)的概念,集合是:
"A cluster of associated objects that are treated as a unit for the purpose of data changes. External references are restricted to one member of the Aggregate, designated as the root. A set of consistency rules applies within the Aggregate's boundaries.".
通常我們是對於domain的每個集合會對應的定義一個repository。也就說,並不是每個實體都會有對應的一個repository。
Repository的接口一般情況下是作為domain model的一部分,但是嚴格意義上講它不屬於domain model。
當我們處理aggregates時,大部分時間我們需要3個常用的相關操作。
1.通過Id得到對應的集合·。
2.向repository添加一個集合。
3.從repository中移除一個集合。
比如我們有個Order表,
那么它的Repository接口IRepository(包含三個基本的方法)如下:
假設我們還有另外一個domain model是Product,它的類如下:
它的Repository接口IProductRepository如下:
可以看出來我們的兩個接口其實有很多代碼是重復的,所以可以來個基本的接口IRepository,並結合System.Collection.Generic中的Generic屬性來設計IRepository的代碼:
對應的IProductRepository和IOrderRepository代碼也得修改:
為了能夠測試我們的Repository,我們這里創建一個Repository繼承IProductRepository接口:
在構造函數中我們先添加product,然后再實現接口的幾個方法。下面來進行單元測試:
運行后可以通過。。。
在NHibernate中使用Repository 模式。
首先我們需要創建一個session provider,如下:
現在需要hibernate.cfg.xml和product的mapping文件:
然后就可以使用單元測試了。
但是實際項目中我們很少使用這種模式。我們來分析下:
當我們有一個私有的方法是GetSession,每次訪問它都會返回一個session實體, 所以Repository中的每個操作都會使用自己的session實體,當我們的項目中不是使用分布式事務時,我們都是希望這些操作都是在一個事務。
為了解決這個問題我們就可以使用UoW模式。