走向.NET架构设计—第三章—分层设计,初涉架构
生活随笔
收集整理的這篇文章主要介紹了
走向.NET架构设计—第三章—分层设计,初涉架构
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
走向.NET架構(gòu)設(shè)計(jì)—分層設(shè)計(jì),初涉架構(gòu)(前篇) 前言:本篇不打算接著上一篇來,這沒有關(guān)系,以為內(nèi)他們之間的聯(lián)系不大,以后我再補(bǔ)上。因?yàn)橹耙恢痹谡務(wù)撛O(shè)計(jì),也談了一些TDD的東西,大家反應(yīng)覺得講述的還是有點(diǎn)”空”,所以打算換一種方式:先講述一些例子,把一些思想穿插著講述,理論的東西最后最為總結(jié)。希望大家支持! 本篇主要講述ASP.NET應(yīng)用中如何進(jìn)行邏輯分層。本篇的前篇會(huì)從Smart UI 反模式和它的一些缺點(diǎn)開始講述,然后一步步的講述如何邏輯分層,而且在后篇中也會(huì)給出一個(gè)ASP.NET設(shè)計(jì)中常用的僅供參考的分層架構(gòu)的Demo。 ?? 一個(gè)穩(wěn)定和易維護(hù)的系統(tǒng)必須建立在一個(gè)好的基礎(chǔ)之上。計(jì)劃和設(shè)計(jì)一個(gè)好的架構(gòu)對(duì)一個(gè)項(xiàng)目的成敗起著至關(guān)重要的作用。可能在我們一般做項(xiàng)目的時(shí)候,經(jīng)驗(yàn)告訴我們:3層,N層的設(shè)計(jì),基本就能把問題解決了,很多的情況確實(shí)是這樣的。在提出一個(gè)設(shè)計(jì)的時(shí)候,常常要考慮為什么要這樣劃分結(jié)構(gòu),而且常常要承擔(dān)風(fēng)險(xiǎn)和責(zé)任,特別是萬一這個(gè)項(xiàng)目因?yàn)樽畛醯脑O(shè)計(jì)而導(dǎo)致崩潰,那就郁悶了。所以設(shè)計(jì)的提出一定和考慮業(yè)務(wù)。 ? 下面就先來看看Smart UI的設(shè)計(jì)方式。 Smart UI 想想我們最初是如何開發(fā)ASP.NET的應(yīng)用的:在頁面設(shè)計(jì)界面中把界面布局好,然后雙擊控件就開始編寫功能代碼。很多的時(shí)候把邏輯判斷和數(shù)據(jù)訪問都寫在頁面的.cs的文件中。后來我們學(xué)習(xí)到了分層,逐漸的明白了這種方式的缺點(diǎn):導(dǎo)致業(yè)務(wù)邏輯代碼到處分散而且重復(fù),不利于以后的更改和維護(hù)等。 ? 盡管有上述說的一些缺點(diǎn),Smart UI還是有它的用途的,如為項(xiàng)目快速的建立一個(gè)原型或者開發(fā)一個(gè)功能比較的小的項(xiàng)目。還有一個(gè)問題,如何最初用Smart UI的方式開發(fā)的小項(xiàng)目很成功,慢慢的變大,變復(fù)雜了,那么很多的問題就出來了。就像Flower在架構(gòu)模式一書中提到的:盡量用領(lǐng)域模型來組織一個(gè)項(xiàng)目的業(yè)務(wù)邏輯,盡管在開始的時(shí)候邏輯不復(fù)雜或者看不出這種方式的好處,一旦項(xiàng)目變化,好處就顯而易見了。在對(duì)項(xiàng)目原型開發(fā)中,盡量不用Smart UI。 ? 其實(shí)Smart UI最大的問題就是:職責(zé)不清—把所有的東西全部寫在一起。 為了和以后講述的內(nèi)容的比較,我還是寫一個(gè)例子出來,很多朋友都已經(jīng)對(duì)這種Smart UI的開發(fā)方式很熟悉了,可以跳過下面的例子。? ? 這里以產(chǎn)品管理系統(tǒng)中的一個(gè)簡單場景為例:顯示所有的產(chǎn)品信息。例子采用ASP.NET來實(shí)現(xiàn),步驟如下: ???? 1.創(chuàng)建AgileSharp.Chapter3.Antipattern的ASP.NET項(xiàng)目,如圖 所示: ? ?????2.? 選擇“App_Data”文件夾,添加數(shù)據(jù)庫“OrderManagement.mdf”,如圖 所示 : ? ? ? ? 3.?在新加的數(shù)據(jù)庫文件上右擊,并且打開。然后添加一個(gè)新表:如下: ? ? 其中ProductId設(shè)置為自動(dòng)標(biāo)示。 然后保存為Products表。 ? 4.?添加一些測試的數(shù)據(jù).? ???? 5.?然后選擇Products表,并且把表拖放到Default.aspx頁面上。這樣之后,在頁面上就自動(dòng)添加一個(gè)GridView和SqlDataSource. ? 界面就如下圖: ? 6.?我我們添加額外的兩列來顯示折扣信息和庫存信息。 ? 7.?然后,我們在Default.aspx.cs后編碼: ? protected?void?gvProduct_RowDataBound(object?sender,GridViewRowEventArgs?e)
{
????if?(e.Row.RowType?==?DataControlRowType.DataRow)
????????{
????????intproductId?=?int.Parse(((System.Data.DataRowView)e.Row.DataItem)
????????[“ProductId”].ToString());
????????decimal?price?=?decimal.Parse(((System.Data.DataRowView)e.Row.DataItem)
????????????[“Price”].ToString());
????????????Label?lblDiscount?=?(Label)e.Row.FindControl(“l(fā)blDiscount”);
????????lblDiscount.Text?=?DisplayDiscount
????(ApplyDiscountsStrategy(productId,?price));
???????}
} 復(fù)制代碼 ???????? 在上面的 GridView1_RowDataBound方法在GridView的每個(gè)row被創(chuàng)建的時(shí)候調(diào)用。這個(gè)方法獲取每個(gè)產(chǎn)品的推薦的零售價(jià)格RRP(Recommend Retail Price),然后調(diào)用 DisplayDiscount和DisplaySavings方法來獲取折扣和庫存,然后再更新UI的顯示。 ???????? 在上面的代碼中,就將計(jì)算折扣和計(jì)算庫存的邏輯寫在了UI中,而且數(shù)據(jù)的訪問代碼也寫在UI中了。這就意味著:如果我們想要在不同的頁面顯示產(chǎn)品的信息,那么這些邏輯就得一遍遍的重寫。如果我們在加一些新的功能,那么頁面后面的代碼就開始修改,開始縫縫補(bǔ)補(bǔ)。 ? 解決Smart UI的方法就是劃分職責(zé),我想大家都知道“單一職責(zé)的原則”,這個(gè)原則不僅僅適用于類,方法,而且對(duì)項(xiàng)目的層次劃分也有作用。分層,最主要的目的就是:把不通的功能放在各自對(duì)應(yīng)的地方,這樣清晰的職責(zé)劃分,也是對(duì)變化點(diǎn)進(jìn)行分離。 下面的圖就是一個(gè)典型的企業(yè)級(jí)ASP.NET項(xiàng)目的分層結(jié)構(gòu): ? 下面我們就來看看,按照我們的一般的分層的經(jīng)驗(yàn)來如何設(shè)計(jì)這功能: ?????下面,我們再次來練下手,重新設(shè)計(jì)前面提到的訂單管理系統(tǒng),使其功能適應(yīng)新的需求(很多時(shí)候,重新設(shè)計(jì)基本不可能的,所以在開始設(shè)計(jì)時(shí)就需要考慮清楚,這里重新設(shè)計(jì)只是從舉例的角度來考慮的): (1) 支持用戶通過網(wǎng)頁或PC桌面程序來訪問系統(tǒng)。 (2) 訂單管理系統(tǒng)需要為其他的系統(tǒng)(如財(cái)務(wù)系統(tǒng)等)提供服務(wù)。 (3) 訂單的一些處理流程和相關(guān)的業(yè)務(wù)處理在其他的系統(tǒng)要用到。 (4) 還有更多的需求,等待確認(rèn)。 ? 注:朋友們一眼就應(yīng)該可以看出,這些類庫的命名是反映了一些DDD的一些概念,但是,不是說在一個(gè)項(xiàng)目的開發(fā)中用了這些概念名詞就表明就開發(fā)的方式是DDD了。 ? 這里我先提一下上面類庫的一起名字:盡管有關(guān)DDD和一些架構(gòu)模式的概念我在以后的文章中會(huì)講,但我這里還是先給大家提一下,目的僅僅是讓大家對(duì)這個(gè)例子有一些更好的了解。 在DDD中,一直主張業(yè)務(wù)模型,也就是我們常常所說的業(yè)務(wù)類,例如之前例子中的Product,只關(guān)注自身的業(yè)務(wù)邏輯,而不管如何去獲取和保存數(shù)據(jù),這些對(duì)數(shù)據(jù)的操作完全交給另外的對(duì)象去執(zhí)行,也就是Repository,這樣就達(dá)到了DDD中所說的PI(Persistence Ignore)。所以在上面的例子中,ASPPatterns.Chap3.Layered.Model就代表了一個(gè)業(yè)務(wù)模型,它之所以被Repository引用,是因?yàn)?/span>Repository負(fù)責(zé)將Model的數(shù)據(jù)持久化到存儲(chǔ)設(shè)備中,而Model不管這些事情了。 ? 在講之前,首先給大家統(tǒng)一 一下Service的概念。? 有時(shí)在類的設(shè)計(jì)過程中,有些行為不適合放在任何的一個(gè)類中,如果把這些行為放在一個(gè)不真正擁有它的類中,只能把類的職責(zé)搞混了。為了給這些行為一個(gè)安置的地方,我們常常把這些行為放在一個(gè)稱為服務(wù)的類中。 ? 作為服務(wù)的類一般沒有狀態(tài)的,可以簡單的作為一個(gè)提供操作接口實(shí)現(xiàn)。 在DDD中,Service也是用來提供一種服務(wù)的。很多人看到了DDD的類層次結(jié)構(gòu)是這樣的:Repository---Model---Service--- Presentation(包括本例),所以都以為Service只能出現(xiàn)在Model的上一層,如果看到Repository-- Service ---Model---Service--- Presentation這樣的層次結(jié)構(gòu),又作何感想。如果被這些所謂的結(jié)構(gòu)搞迷惑了,那就說明對(duì)DDD的理解只是在于“形”上。Service就是向外部提供的功能接口,和我們常見的Web Service的概念很相似,例如的Web Service就是向外部系統(tǒng)提供一些功能的。 我們來看下面的一個(gè)圖: ? ? ? 有時(shí)候之所以要在Model層之上加上一個(gè)Service層,主要的原因就是實(shí)現(xiàn)粗顆粒度的API,往往和系統(tǒng)的User Case有一定的聯(lián)系。例如,如果在系統(tǒng)用例中要實(shí)現(xiàn)一個(gè)用戶訂單的處理,那么可能就涉及到Customer, Product,Order等類,當(dāng)然,如果我們調(diào)用這些類來共同完成這個(gè)任務(wù)是沒問題的,但是這樣就向調(diào)用者暴露這些類之間的復(fù)雜的關(guān)系,而且如果處理的過程變化了,那么調(diào)用者的代碼就要改變,如果把這個(gè)處理的方法放在上面的任意一個(gè)類中,又顯得不倫不類,這里的Service功能就類似于設(shè)計(jì)模式中的Fa?ade外觀模式。這樣就向外界提供簡單的API,向外界提供訂單處理的服務(wù)! ? 所以在一般在DDD中業(yè)務(wù)層被劃分為兩個(gè)邏輯層:Model (提供細(xì)粒度的業(yè)務(wù)邏輯處理,也便于重用), Service(提供業(yè)務(wù)處理的流程,提供粗顆粒度的供外部調(diào)用的方法)。 ? 但是,我們常見到的Model層之上的Service層僅僅只是對(duì)CRUD的再次封裝,一個(gè)可能的原因就是業(yè)務(wù)不是很復(fù)雜,這時(shí)其實(shí)這個(gè)Service層可以拿掉的,但是考慮到以后可能邏輯會(huì)更多更復(fù)雜,所以還是保留Service這層。 ? 其實(shí)在Repository上的那個(gè)Service也是同樣的概念。例如發(fā)送郵件通知用戶的功能。例如上圖中的最上層的Service可以調(diào)用業(yè)務(wù)層和基礎(chǔ)設(shè)施層的Service來共同完成一個(gè)事情。 ? 今天的上篇的就講述到這里吧,下篇會(huì)用一個(gè)例子,代碼量還是有點(diǎn)的!敬請關(guān)注! 大家感興趣,我們下篇講述!
{
????if?(e.Row.RowType?==?DataControlRowType.DataRow)
????????{
????????intproductId?=?int.Parse(((System.Data.DataRowView)e.Row.DataItem)
????????[“ProductId”].ToString());
????????decimal?price?=?decimal.Parse(((System.Data.DataRowView)e.Row.DataItem)
????????????[“Price”].ToString());
????????????Label?lblDiscount?=?(Label)e.Row.FindControl(“l(fā)blDiscount”);
????????lblDiscount.Text?=?DisplayDiscount
????(ApplyDiscountsStrategy(productId,?price));
???????}
} 復(fù)制代碼 ???????? 在上面的 GridView1_RowDataBound方法在GridView的每個(gè)row被創(chuàng)建的時(shí)候調(diào)用。這個(gè)方法獲取每個(gè)產(chǎn)品的推薦的零售價(jià)格RRP(Recommend Retail Price),然后調(diào)用 DisplayDiscount和DisplaySavings方法來獲取折扣和庫存,然后再更新UI的顯示。 ???????? 在上面的代碼中,就將計(jì)算折扣和計(jì)算庫存的邏輯寫在了UI中,而且數(shù)據(jù)的訪問代碼也寫在UI中了。這就意味著:如果我們想要在不同的頁面顯示產(chǎn)品的信息,那么這些邏輯就得一遍遍的重寫。如果我們在加一些新的功能,那么頁面后面的代碼就開始修改,開始縫縫補(bǔ)補(bǔ)。 ? 解決Smart UI的方法就是劃分職責(zé),我想大家都知道“單一職責(zé)的原則”,這個(gè)原則不僅僅適用于類,方法,而且對(duì)項(xiàng)目的層次劃分也有作用。分層,最主要的目的就是:把不通的功能放在各自對(duì)應(yīng)的地方,這樣清晰的職責(zé)劃分,也是對(duì)變化點(diǎn)進(jìn)行分離。 下面的圖就是一個(gè)典型的企業(yè)級(jí)ASP.NET項(xiàng)目的分層結(jié)構(gòu): ? 下面我們就來看看,按照我們的一般的分層的經(jīng)驗(yàn)來如何設(shè)計(jì)這功能: ?????下面,我們再次來練下手,重新設(shè)計(jì)前面提到的訂單管理系統(tǒng),使其功能適應(yīng)新的需求(很多時(shí)候,重新設(shè)計(jì)基本不可能的,所以在開始設(shè)計(jì)時(shí)就需要考慮清楚,這里重新設(shè)計(jì)只是從舉例的角度來考慮的): (1) 支持用戶通過網(wǎng)頁或PC桌面程序來訪問系統(tǒng)。 (2) 訂單管理系統(tǒng)需要為其他的系統(tǒng)(如財(cái)務(wù)系統(tǒng)等)提供服務(wù)。 (3) 訂單的一些處理流程和相關(guān)的業(yè)務(wù)處理在其他的系統(tǒng)要用到。 (4) 還有更多的需求,等待確認(rèn)。 ? 注:朋友們一眼就應(yīng)該可以看出,這些類庫的命名是反映了一些DDD的一些概念,但是,不是說在一個(gè)項(xiàng)目的開發(fā)中用了這些概念名詞就表明就開發(fā)的方式是DDD了。 ? 這里我先提一下上面類庫的一起名字:盡管有關(guān)DDD和一些架構(gòu)模式的概念我在以后的文章中會(huì)講,但我這里還是先給大家提一下,目的僅僅是讓大家對(duì)這個(gè)例子有一些更好的了解。 在DDD中,一直主張業(yè)務(wù)模型,也就是我們常常所說的業(yè)務(wù)類,例如之前例子中的Product,只關(guān)注自身的業(yè)務(wù)邏輯,而不管如何去獲取和保存數(shù)據(jù),這些對(duì)數(shù)據(jù)的操作完全交給另外的對(duì)象去執(zhí)行,也就是Repository,這樣就達(dá)到了DDD中所說的PI(Persistence Ignore)。所以在上面的例子中,ASPPatterns.Chap3.Layered.Model就代表了一個(gè)業(yè)務(wù)模型,它之所以被Repository引用,是因?yàn)?/span>Repository負(fù)責(zé)將Model的數(shù)據(jù)持久化到存儲(chǔ)設(shè)備中,而Model不管這些事情了。 ? 在講之前,首先給大家統(tǒng)一 一下Service的概念。? 有時(shí)在類的設(shè)計(jì)過程中,有些行為不適合放在任何的一個(gè)類中,如果把這些行為放在一個(gè)不真正擁有它的類中,只能把類的職責(zé)搞混了。為了給這些行為一個(gè)安置的地方,我們常常把這些行為放在一個(gè)稱為服務(wù)的類中。 ? 作為服務(wù)的類一般沒有狀態(tài)的,可以簡單的作為一個(gè)提供操作接口實(shí)現(xiàn)。 在DDD中,Service也是用來提供一種服務(wù)的。很多人看到了DDD的類層次結(jié)構(gòu)是這樣的:Repository---Model---Service--- Presentation(包括本例),所以都以為Service只能出現(xiàn)在Model的上一層,如果看到Repository-- Service ---Model---Service--- Presentation這樣的層次結(jié)構(gòu),又作何感想。如果被這些所謂的結(jié)構(gòu)搞迷惑了,那就說明對(duì)DDD的理解只是在于“形”上。Service就是向外部提供的功能接口,和我們常見的Web Service的概念很相似,例如的Web Service就是向外部系統(tǒng)提供一些功能的。 我們來看下面的一個(gè)圖: ? ? ? 有時(shí)候之所以要在Model層之上加上一個(gè)Service層,主要的原因就是實(shí)現(xiàn)粗顆粒度的API,往往和系統(tǒng)的User Case有一定的聯(lián)系。例如,如果在系統(tǒng)用例中要實(shí)現(xiàn)一個(gè)用戶訂單的處理,那么可能就涉及到Customer, Product,Order等類,當(dāng)然,如果我們調(diào)用這些類來共同完成這個(gè)任務(wù)是沒問題的,但是這樣就向調(diào)用者暴露這些類之間的復(fù)雜的關(guān)系,而且如果處理的過程變化了,那么調(diào)用者的代碼就要改變,如果把這個(gè)處理的方法放在上面的任意一個(gè)類中,又顯得不倫不類,這里的Service功能就類似于設(shè)計(jì)模式中的Fa?ade外觀模式。這樣就向外界提供簡單的API,向外界提供訂單處理的服務(wù)! ? 所以在一般在DDD中業(yè)務(wù)層被劃分為兩個(gè)邏輯層:Model (提供細(xì)粒度的業(yè)務(wù)邏輯處理,也便于重用), Service(提供業(yè)務(wù)處理的流程,提供粗顆粒度的供外部調(diào)用的方法)。 ? 但是,我們常見到的Model層之上的Service層僅僅只是對(duì)CRUD的再次封裝,一個(gè)可能的原因就是業(yè)務(wù)不是很復(fù)雜,這時(shí)其實(shí)這個(gè)Service層可以拿掉的,但是考慮到以后可能邏輯會(huì)更多更復(fù)雜,所以還是保留Service這層。 ? 其實(shí)在Repository上的那個(gè)Service也是同樣的概念。例如發(fā)送郵件通知用戶的功能。例如上圖中的最上層的Service可以調(diào)用業(yè)務(wù)層和基礎(chǔ)設(shè)施層的Service來共同完成一個(gè)事情。 ? 今天的上篇的就講述到這里吧,下篇會(huì)用一個(gè)例子,代碼量還是有點(diǎn)的!敬請關(guān)注! 大家感興趣,我們下篇講述!
總結(jié)
以上是生活随笔為你收集整理的走向.NET架构设计—第三章—分层设计,初涉架构的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 公积金每个月都可以提取吗
- 下一篇: 【译】Withdrawal sympto