走向.NET架構設計—分層設計,初涉架構(前篇)
前言:本篇不打算接着上一篇來,這沒有關系,以為内他們之間的聯系不大,以後我再補上。因為之前一直在談論設計,也談了一些TDD的東西,大家反應覺得講述的還是有點”空”,是以打算換一種方式:先講述一些例子,把一些思想穿插着講述,理論的東西最後最為總結。希望大家支援!
本篇主要講述ASP.NET應用中如何進行邏輯分層。本篇的前篇會從Smart UI 反模式和它的一些缺點開始講述,然後一步步的講述如何邏輯分層,而且在後篇中也會給出一個ASP.NET設計中常用的僅供參考的分層架構的Demo。
一個穩定和易維護的系統必須建立在一個好的基礎之上。計劃和設計一個好的架構對一個項目的成敗起着至關重要的作用。可能在我們一般做項目的時候,經驗告訴我們:3層,N層的設計,基本就能把問題解決了,很多的情況确實是這樣的。在提出一個設計的時候,常常要考慮為什麼要這樣劃分結構,而且常常要承擔風險和責任,特别是萬一這個項目因為最初的設計而導緻崩潰,那就郁悶了。是以設計的提出一定和考慮業務。
下面就先來看看Smart UI的設計方式。
Smart UI
想想我們最初是如何開發ASP.NET的應用的:在頁面設計界面中把界面布局好,然後輕按兩下控件就開始編寫功能代碼。很多的時候把邏輯判斷和資料通路都寫在頁面的.cs的檔案中。後來我們學習到了分層,逐漸的明白了這種方式的缺點:導緻業務邏輯代碼到處分散而且重複,不利于以後的更改和維護等。
盡管有上述說的一些缺點,Smart UI還是有它的用途的,如為項目快速的建立一個原型或者開發一個功能比較的小的項目。還有一個問題,如何最初用Smart UI的方式開發的小項目很成功,慢慢的變大,變複雜了,那麼很多的問題就出來了。就像Flower在架構模式一書中提到的:盡量用領域模型來組織一個項目的業務邏輯,盡管在開始的時候邏輯不複雜或者看不出這種方式的好處,一旦項目變化,好處就顯而易見了。在對項目原型開發中,盡量不用Smart UI。
其實Smart UI最大的問題就是:職責不清—把所有的東西全部寫在一起。
為了和以後講述的内容的比較,我還是寫一個例子出來,很多朋友都已經對這種Smart UI的開發方式很熟悉了,可以跳過下面的例子。
這裡以産品管理系統中的一個簡單場景為例:顯示所有的産品資訊。例子采用ASP.NET來實作,步驟如下:
1.建立AgileSharp.Chapter3.Antipattern的ASP.NET項目,如圖 所示:
2. 選擇“App_Data”檔案夾,添加資料庫“OrderManagement.mdf”,如圖 所示 :
3. 在新加的資料庫檔案上右擊,并且打開。然後添加一個新表:如下:
其中ProductId設定為自動标示。
然後儲存為Products表。
4. 添加一些測試的資料.
5. 然後選擇Products表,并且把表拖放到Default.aspx頁面上。這樣之後,在頁面上就自動添加一個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(“lblDiscount”);
lblDiscount.Text = DisplayDiscount
(ApplyDiscountsStrategy(productId, price));
}
}
在上面的 GridView1_RowDataBound方法在GridView的每個row被建立的時候調用。這個方法擷取每個産品的推薦的零售價格RRP(Recommend Retail Price),然後調用 DisplayDiscount和DisplaySavings方法來擷取折扣和庫存,然後再更新UI的顯示。
在上面的代碼中,就将計算折扣和計算庫存的邏輯寫在了UI中,而且資料的通路代碼也寫在UI中了。這就意味着:如果我們想要在不同的頁面顯示産品的資訊,那麼這些邏輯就得一遍遍的重寫。如果我們在加一些新的功能,那麼頁面後面的代碼就開始修改,開始縫縫補補。
解決Smart UI的方法就是劃分職責,我想大家都知道“單一職責的原則”,這個原則不僅僅适用于類,方法,而且對項目的層次劃分也有作用。分層,最主要的目的就是:把不通的功能放在各自對應的地方,這樣清晰的職責劃分,也是對變化點進行分離。
下面的圖就是一個典型的企業級ASP.NET項目的分層結構:
下面我們就來看看,按照我們的一般的分層的經驗來如何設計這功能:
下面,我們再次來練下手,重新設計前面提到的訂單管理系統,使其功能适應新的需求(很多時候,重新設計基本不可能的,是以在開始設計時就需要考慮清楚,這裡重新設計隻是從舉例的角度來考慮的):
(1) 支援使用者通過網頁或PC桌面程式來通路系統。
(2) 訂單管理系統需要為其他的系統(如财務系統等)提供服務。
(3) 訂單的一些處理流程和相關的業務處理在其他的系統要用到。
(4) 還有更多的需求,等待确認。
注:朋友們一眼就應該可以看出,這些類庫的命名是反映了一些DDD的一些概念,但是,不是說在一個項目的開發中用了這些概念名詞就表明就開發的方式是DDD了。
這裡我先提一下上面類庫的一起名字:盡管有關DDD和一些架構模式的概念我在以後的文章中會講,但我這裡還是先給大家提一下,目的僅僅是讓大家對這個例子有一些更好的了解。
在DDD中,一直主張業務模型,也就是我們常常所說的業務類,例如之前例子中的Product,隻關注自身的業務邏輯,而不管如何去擷取和儲存資料,這些對資料的操作完全交給另外的對象去執行,也就是Repository,這樣就達到了DDD中所說的PI(Persistence Ignore)。是以在上面的例子中,ASPPatterns.Chap3.Layered.Model就代表了一個業務模型,它之是以被Repository引用,是因為Repository負責将Model的資料持久化到儲存設備中,而Model不管這些事情了。
在講之前,首先給大家統一 一下Service的概念。
有時在類的設計過程中,有些行為不适合放在任何的一個類中,如果把這些行為放在一個不真正擁有它的類中,隻能把類的職責搞混了。為了給這些行為一個安置的地方,我們常常把這些行為放在一個稱為服務的類中。
作為服務的類一般沒有狀态的,可以簡單的作為一個提供操作接口實作。
在DDD中,Service也是用來提供一種服務的。很多人看到了DDD的類層次結構是這樣的:Repository---Model---Service--- Presentation(包括本例),是以都以為Service隻能出現在Model的上一層,如果看到Repository-- Service ---Model---Service--- Presentation這樣的層次結構,又作何感想。如果被這些所謂的結構搞迷惑了,那就說明對DDD的了解隻是在于“形”上。Service就是向外部提供的功能接口,和我們常見的Web Service的概念很相似,例如的Web Service就是向外部系統提供一些功能的。
我們來看下面的一個圖:
有時候之是以要在Model層之上加上一個Service層,主要的原因就是實作粗顆粒度的API,往往和系統的User Case有一定的聯系。例如,如果在系統用例中要實作一個使用者訂單的處理,那麼可能就涉及到Customer, Product,Order等類,當然,如果我們調用這些類來共同完成這個任務是沒問題的,但是這樣就向調用者暴露這些類之間的複雜的關系,而且如果處理的過程變化了,那麼調用者的代碼就要改變,如果把這個處理的方法放在上面的任意一個類中,又顯得不倫不類,這裡的Service功能就類似于設計模式中的Façade外觀模式。這樣就向外界提供簡單的API,向外界提供訂單處理的服務!
是以在一般在DDD中業務層被劃分為兩個邏輯層:Model (提供細粒度的業務邏輯處理,也便于重用), Service(提供業務處理的流程,提供粗顆粒度的供外部調用的方法)。
但是,我們常見到的Model層之上的Service層僅僅隻是對CRUD的再次封裝,一個可能的原因就是業務不是很複雜,這時其實這個Service層可以拿掉的,但是考慮到以後可能邏輯會更多更複雜,是以還是保留Service這層。
其實在Repository上的那個Service也是同樣的概念。例如發送郵件通知使用者的功能。例如上圖中的最上層的Service可以調用業務層和基礎設施層的Service來共同完成一個事情。
今天的上篇的就講述到這裡吧,下篇會用一個例子,代碼量還是有點的!敬請關注!
大家感興趣,我們下篇講述!
本文轉自yanyangtian51CTO部落格,原文連結:
http://blog.51cto.com/yanyangtian/410222
,如需轉載請自行聯系原作者