|
這個問題來源于Apworks應(yīng)用開發(fā)框架的設(shè)計。由于命令與查詢職責的分離,使得基于CQRS體系結(jié)構(gòu)風格的應(yīng)用系統(tǒng)的外部存儲系統(tǒng)的結(jié)構(gòu)變得簡單起來:在“命令”部分,簡單地說,只需要 Event Store和Snapshot Store來保存Domain Model;而“查詢”部分,則又是基于事件派送與偵聽的系統(tǒng)集成。之前我也提到過,“查詢”部分由于不牽涉到Domain Model,于是,它的設(shè)計應(yīng)該隨意性很大:不需要受到Domain Model的牽制,例如,我們可以根據(jù)UI所需的數(shù)據(jù)源結(jié)構(gòu)進行“查詢”庫的設(shè)計。Greg Young在他的“CQRS Documents”一文中也提到了這樣一些相關(guān)話題:就“查詢”部分而言,雖然也存在“阻抗失衡”效應(yīng),但是事件模型與關(guān)系模型之間的這種效應(yīng),要遠遠小于對象模型與關(guān)系模型之間的“阻抗失衡”效應(yīng)。這是因為,事件模型本身沒有結(jié)構(gòu),它僅僅表述“該對關(guān)系模型做哪些操作”這樣的概念。在設(shè)計上,Greg Young建議,采用一種非正規(guī)的方式設(shè)計“查詢”數(shù)據(jù)庫,以便盡量減少讀取數(shù)據(jù)時所需的JOIN操作,比如可以選用基于第一范式(1NF)的關(guān)系模型。這是一種反范式模型,雖然Greg Young并沒有建議根據(jù)UI所需的數(shù)據(jù)源結(jié)構(gòu)進行設(shè)計,但思想是相同的:基于事件而不拘泥于對象模型本身。由此引申出來的另一個好處就是外部存儲系統(tǒng)架構(gòu)的隨意性:你可以選用任何存儲技術(shù)和存儲媒介,這又給基于系統(tǒng)架構(gòu)的性能優(yōu)化提供了便利。因為并非所有的存儲架構(gòu)都支持“表”、“字段”、“存儲過程”、“JOIN”這些概念。
根據(jù)上面的簡單分析,我們得到一個結(jié)論:通常情況下,或許基于CQRS體系結(jié)構(gòu)風格的應(yīng)用系統(tǒng)更多的是采用的“平整”的外部存儲結(jié)構(gòu),簡而言之,就是一個數(shù)據(jù)訪問對象(DAO)對應(yīng)一張數(shù)據(jù)表。這也是我所設(shè)計的Apworks應(yīng)用開發(fā)框架中默認支持的一種存儲結(jié)構(gòu)。有讀過Apworks源代碼的朋友會發(fā)現(xiàn),在Apworks.Events.Storage命名空間下,有兩個定制的DAO:DomainEventDataObject,用于表述領(lǐng)域事件的數(shù)據(jù)結(jié)構(gòu),以及SnapshotDataObject,用于表述快照的數(shù)據(jù)結(jié)構(gòu),與之相對應(yīng)的就是數(shù)據(jù)庫中的兩張表:DomainEvents和 Snapshots。雖然結(jié)構(gòu)變得這么簡單,但是映射關(guān)系總還是需要維護的:最簡單的就是需要在對象類型名稱與數(shù)據(jù)表名之間,以及對象屬性與數(shù)據(jù)表字段之間建立起映射關(guān)系。在Apworks中,這種映射關(guān)系是由Apworks.Storage.IStorageMappingResolver接口完成的。有關(guān)這個接口的內(nèi)容不是本文討論的重點,暫且不深入分析了。
至此,也許你不會接受我上面的討論,認為“基于UI設(shè)計數(shù)據(jù)庫結(jié)構(gòu)”或者“采用1NF、反范式設(shè)計數(shù)據(jù)庫結(jié)構(gòu)”是無法接受的,那么,接下來的討論可能對你來說意義也不大了。因為下面的問題是以上面的描述為基礎(chǔ)的:一個數(shù)據(jù)訪問對象對應(yīng)一張數(shù)據(jù)表。不過即使你不認同我的觀點,我也建議你繼續(xù)看完本文。
Query Object模式
雖然只是簡單的映射,但畢竟不能忽略這樣的映射關(guān)系。Apworks作為一個應(yīng)用開發(fā)框架,需要提供方便的整合接口,以便今后能夠根據(jù)不同的客戶需求進行擴展。例如在存儲部分,數(shù)據(jù)的增刪改查(CRUD)是基于數(shù)據(jù)訪問對象(DAO)的,這樣做的一個好處是能夠?qū)ν獠?a href=/pingce/cunchu/ target=_blank class=infotextkey>存儲系統(tǒng)進行抽象,使得訪問存儲系統(tǒng)的部分能夠無需關(guān)系存儲系統(tǒng)的細節(jié)問題??蛻粲锌赡苓x擇SQL Server、Oracle、MySQL等關(guān)系型數(shù)據(jù)庫作為存儲系統(tǒng),也可以選擇其它的非關(guān)系型數(shù)據(jù)庫作為存儲系統(tǒng),因此,我們的設(shè)計不能僅僅局限于關(guān)系型數(shù)據(jù)庫,我們需要同時考慮其它形式的數(shù)據(jù)存儲產(chǎn)品以便將來能夠方便地集成新的存儲方案。假設(shè)我們要設(shè)計一個針對 DomainEventDataObject的“查詢”功能,我們需要考慮的問題可能會有(但不一定僅限于):
* 需要查詢對象的哪些屬性(或者說與DomainEventDataObject相對應(yīng)的數(shù)據(jù)表的哪些字段)
* 需要根據(jù)什么樣的條件進行查詢
* 查詢是否需要排序
* 是否只查結(jié)果集中的任意一條記錄,還是要返回所有的記錄
在Apworks框架的Alpha版本中,查詢的方法定義在Apworks.Storage.IStorage接口中。比如,根據(jù)給定的查詢條件和排序方式,對指定DAO進行查詢的方法定義如下:
/// <summary>
/// Gets a list of ordered objects from storage by given selection criteria and order.
/// </summary>
/// <typeparam name="T">The type of the object to get.</typeparam>
/// <param name="criteria">The <c>PropertyBag</c> instance which contains the criteria.</param>
/// <param name="orders">The <c>PropertyBag</c> instance which contains the ordering fields.</param>
/// <param name="sortOrder">The sort order.</param>
/// <returns>A list of ordered objects.</returns>
IEnumerable<T> Select<T>(PropertyBag criteria, PropertyBag orders, SortOrder sortOrder)
where T : class, new();
NET技術(shù):使用LINQ Expression構(gòu)建Query Object,轉(zhuǎn)載需保留來源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。