《Pro ASP.NET MVC 3 Framework》学习笔记之四【领域模型介绍】
主題:應(yīng)用領(lǐng)域驅(qū)動(dòng)開發(fā)(Applying Domain-Driven Development)
Domain Model是MVC程序的"心臟",其他的一切,包括Controllers和Views僅僅是用來跟Domain Model交互的一種方式,ASP.NET MVC并沒有限制使用在Domain Model上面的技術(shù),我們可以自由的選擇跟.net framework交互的技術(shù),并且這樣的選擇是非常多的。不僅如此,ASP.NET MVC為我們提供了基礎(chǔ)的架構(gòu)和約定來幫助Domain Model里面的Classes跟Controllers和Views的聯(lián)系,也包括跟MVC自己的聯(lián)系。
它們有三個(gè)關(guān)鍵的功能,如下所示:
a.模型綁定(Model Binding):是自動(dòng)使用HTML表單提交的數(shù)據(jù)來組織領(lǐng)域?qū)ο?Domain Objects)基礎(chǔ)的約定。
b.模型元數(shù)據(jù)(Model metadata):描述了Model Classes對.net的所要表達(dá)的意思。舉例而言,就是給屬性賦予一個(gè)人容易理解的表述或者是對屬性呈現(xiàn)時(shí)的一些提示。asp.net mvc能夠自動(dòng)的識別并對Model Classes合理的呈現(xiàn)到Views里面。
c.驗(yàn)證(Validation):能夠在模型綁定時(shí)被執(zhí)行,并且能夠應(yīng)用被定義為元素?fù)?jù)的那些規(guī)則。
如果你對于模型綁定(Model Binding)的理解也跟我一樣還不是很清楚,也沒有必要著急,更不要放棄對MVC的學(xué)習(xí),因?yàn)闀暮竺嬲鹿?jié)會(huì)有詳細(xì)的講解 ,呵呵。現(xiàn)在我們先將ASP.NET MVC的實(shí)現(xiàn)放到一邊,單純思考下領(lǐng)域建模(Domain Modelling),下面使用.net和sql server創(chuàng)建一個(gè)簡單的競拍程序的領(lǐng)域模型。
一,創(chuàng)建一個(gè)簡單的領(lǐng)域模型
下面是要?jiǎng)?chuàng)建的競拍程序的類圖
上面的模型包含了一個(gè)Members的集合,每一個(gè)Member會(huì)有一個(gè)Bids集合,每一個(gè)Bid對應(yīng)一個(gè)Item,每一個(gè)Item可以有多個(gè)來自不同Members的Bids
實(shí)現(xiàn)我們自己的domain model并作為一個(gè)獨(dú)立的組件其中一個(gè)關(guān)鍵的地方是我們選擇的語言和術(shù)語,這個(gè)不是我們的編程語言,而是領(lǐng)域建模的通用的語言。這種語言是開發(fā)人員和領(lǐng)域的專業(yè)人員都知道的,這樣主要是為了這兩種人能流暢的交流,而這卻是至關(guān)重要的。當(dāng)領(lǐng)域的專家們不了解建模的一些概念時(shí),我們應(yīng)該針對使用的術(shù)語達(dá)成一個(gè)共識,這個(gè)共識就是創(chuàng)建一種通用的語言并貫穿在整個(gè)領(lǐng)域建模過程中。這樣做有很多的好處。
1.首先,開發(fā)人員傾向于使用編程語言,比如類名,數(shù)據(jù)庫等等名詞來表達(dá)。而業(yè)務(wù)專家們是不懂這些的,他們也不需要懂。業(yè)務(wù)專家知曉一些技術(shù)方面的知識是一件非常危險(xiǎn)的事情,因?yàn)樗麄儠?huì)經(jīng)常根據(jù)自己對技術(shù)的理解來不斷篩選他們的需求,這也就意味著需求會(huì)頻繁的更改,進(jìn)而導(dǎo)致開發(fā)人員也不知道業(yè)務(wù)專家的真實(shí)需求到底是什么。創(chuàng)建通用語言的方法能夠幫助我們避免在一個(gè)應(yīng)用程序里面過度的泛化需求,程序員傾向于建立每一個(gè)可能業(yè)務(wù)實(shí)際模型,而不是具體到某一個(gè)業(yè)務(wù)需求。
2.在通用語言和領(lǐng)域模型之間的這種連接不應(yīng)該是非常膚淺的而是向DDD(Domain-Driven Design)專家所建議的那樣:對通用語言的任何變化都會(huì)導(dǎo)致Model的變化。假如我們讓建模跟業(yè)務(wù)領(lǐng)域不同步,我們就需要建立一種從Model到domain映射的中間語言,從長遠(yuǎn)來看,這種做法會(huì)導(dǎo)致災(zāi)難。為此,我們將創(chuàng)建一個(gè)會(huì)兩種語言的特殊人類,他們隨后就開始篩選需求,這卻是建立在他們對兩種語言都不完全理解的基礎(chǔ)之上,當(dāng)然這樣的后果可以想象。
二,聚合與簡化
上面的圖,為我們提供了一個(gè)很好的建模起點(diǎn)
但是上面圖示的模型并沒有提供用C#和SQL Server實(shí)現(xiàn)Model的任何有用的幫助,會(huì)有不少的問題困擾我們:
1.如果我們load一個(gè)Member進(jìn)入內(nèi)存,也是不是應(yīng)該load Member的Bids以及相關(guān)的Items進(jìn)入內(nèi)存呢?
2.如果我們這樣做了,我們是否需要將這個(gè)Item其他的bids也load進(jìn)內(nèi)存,并且也將做這些Bids的Members一同load進(jìn)內(nèi)存呢?
3.當(dāng)我們刪除一個(gè)對象時(shí),我是否應(yīng)該刪除相關(guān)的對象呢?如果是,又有哪些呢?
4.如果我們選擇用文檔存儲(chǔ)代替關(guān)系型數(shù)據(jù)庫來持久化,那哪一個(gè)對象的集合應(yīng)該呈現(xiàn)到同一個(gè)文檔呢?
所有以上的問題,我們不知道作何解答,并且我們的領(lǐng)域模型也沒有給我們?nèi)魏未鸢浮?/p>
回答這些問題的DDD方式是將Domain Objects分配到組里面,這種方式稱為聚合(aggregates)。
下圖很好的展示了怎樣聚合在我們這個(gè)競拍程序里面的領(lǐng)域模型,如下所示:
一個(gè)聚合的實(shí)體組將若干領(lǐng)域?qū)ο舐?lián)合(Together)到了一起,有一個(gè)根實(shí)體被用來標(biāo)識整個(gè)聚合,它在驗(yàn)證和持久化操作里面充當(dāng)了"boss"的角色。在數(shù)據(jù)變化時(shí),我把聚合當(dāng)作一個(gè)單元來統(tǒng)籌處理,所以我們需要?jiǎng)?chuàng)建呈現(xiàn)在領(lǐng)域模型上下文里面有意義的關(guān)系的聚合,并且創(chuàng)建跟實(shí)現(xiàn)業(yè)務(wù)過程一致的邏輯操作。也就是說,我們需要通過分組對象來創(chuàng)建聚合,而這些對象是可以作為一個(gè)組被改變的。
一個(gè)非常重要的DDD規(guī)則是,在一個(gè)聚合實(shí)例范圍外的對象,只能通過對根實(shí)體(root entity)的引用來持久化,而不是引用在聚合里面的對象。這條規(guī)則強(qiáng)化了將聚合里面的對象作為一個(gè)單元來對待的概念。在本例子里面,Members和Items都是聚合的根,而Bids只能在作為它們聚合根實(shí)體的Item的上下文(the context of Item)里面被訪問。Bids可以引用Members(根實(shí)體),但是Members不能引用Bids(不是根實(shí)體)
聚合的一個(gè)好處是簡化了對象跟領(lǐng)域模型之間的關(guān)系,通常這樣能夠幫助我們對需要建模的領(lǐng)域的本質(zhì)的理解。本質(zhì)上講,創(chuàng)建聚合約束領(lǐng)域模型和對象之間的關(guān)系使得這種關(guān)系更加接近于現(xiàn)實(shí)領(lǐng)域里面存在的關(guān)系。下面是用C#來表達(dá)的,如下所示:
public class Member {public string LoginName { get; set; } // The unique key
public int ReputationPoints { get; set; }
}
public class Item {
public int ItemID { get; private set; } // The unique key
public string Title { get; set; }
public string Description { get; set; }
public DateTime AuctionEndDate { get; set; }
public IList<Bid> Bids { get; set; }
}
public class Bid {
public Member Member { get; set; }
public DateTime DatePlaced { get; set; }
public decimal BidAmount { get; set; }
}
從上面的代碼可以看出,我們很容易就捕捉到了Bids和Members之間的單向關(guān)系的本質(zhì),當(dāng)然我們也可以建立一些其他的約束。例如:Bids是不可變的,這也是符合實(shí)際的。應(yīng)用聚合能夠幫助我們建立更加有用,更加精確的領(lǐng)域模型,也能夠讓我們用C#熟練的實(shí)現(xiàn)。
一般來講,聚合為一個(gè)領(lǐng)域模型增加了結(jié)構(gòu)化和精確化。這也使得應(yīng)用驗(yàn)證變得容易(根實(shí)體負(fù)責(zé)驗(yàn)證聚合里面所有對象的的狀態(tài)),這很明顯也是持久化的單元。由于聚合的本質(zhì)就是領(lǐng)域模型的原子單元,它們也能夠適用事務(wù)管理的單元和級聯(lián)從數(shù)據(jù)庫刪除的單元。
另一方面,聚合常常是人為加上限制。聚合(Aggregates)的概念能夠很自然的從文檔型數(shù)據(jù)庫得到,但它不是sqlserver本身的概念,也不是存在大部分ORM工具里的概念,為了很好的實(shí)現(xiàn)它們,我們的團(tuán)隊(duì)需要科學(xué)有效的溝通。
三,定義倉庫(Defining Repositories)
在某些時(shí)候,我們需要給領(lǐng)域模型添加持久化,這通常是通過關(guān)系型,對象型,或文檔型的數(shù)據(jù)庫來做。持久化不是我們領(lǐng)域模型的一部分,它是一個(gè)獨(dú)立的關(guān)注點(diǎn),這也就意味著我們不能將持久化的代碼跟定義領(lǐng)域模型的代碼混合到一起。解決這個(gè)問題通常的方式就是定義一個(gè)倉庫(Repositories)
Respositories是基于數(shù)據(jù)庫(可能你選擇的是文件存儲(chǔ)等等)層面的對象呈現(xiàn)。領(lǐng)域模型通過調(diào)用定義在Repositories的方法來間接的存儲(chǔ)和查詢數(shù)據(jù)庫,這使得我們的Model可以獨(dú)立于持久化的實(shí)現(xiàn)。這樣約定就是為每一個(gè)聚合(Aggregates)定義單獨(dú)的數(shù)據(jù)模型。在我們的競拍程序里面,我們可以創(chuàng)建2個(gè)Repositories,它們分別是針對Members的Repository和針對Items的Repository。注意這里,我們并不需要?jiǎng)?chuàng)建針對Bids的Repository,因?yàn)锽ids會(huì)作為Items聚會(huì)持久化的一部分)。
下面是定義的兩個(gè)Repositories,如下所示:
public class MembersRepository {public void AddMember(Member member) { ... }
public Member FetchByLoginName(string loginName) { ... }
public void SubmitChanges() { ...}
}
public class ItemsRepository {
public void AddItem(Item item) { ...}
public Item FetchByID(int itemID) { ... }
public IList<Item> ListItems(int pageSize,int pageIndex) { ...}
public void SubmitChanges() { ... }
}
特別需要注意:Repositories僅僅針對Loading和Saving Data.它們不包含任何其他的邏輯。
今天的筆記做到這里,我也是剛學(xué)習(xí)MVC,做筆記是為了鞏固和加深理解,當(dāng)然如果能給到那些跟我一樣的初學(xué)者一點(diǎn)點(diǎn)的幫助,我就非常高興了。筆記里面肯定會(huì)有我理解不對的地方,還請路過的大牛們多多幫助指導(dǎo)。謝謝!
祝路過的朋友工作順利!
晚安!
轉(zhuǎn)載于:https://www.cnblogs.com/mszhangxuefei/archive/2011/12/04/mvcnotes_4.html
總結(jié)
以上是生活随笔為你收集整理的《Pro ASP.NET MVC 3 Framework》学习笔记之四【领域模型介绍】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 地下城与勇士的图标为什么没有了
- 下一篇: 面试题-ASP 与 ASP.Net的区别