2011-03-30 24 views
5

我正在向我們的網站添加功能,該功能使用MSMQ異步執行長時間運行的流程。然而,這樣做意味着我們需要在用戶請求完成時通知用戶。使用命令模式,我創建了一個名爲INotify的接口並將其組合到消息類中,因此消息處理類可以簡單地在消息的INotify對象上調用GiveNotice()。第一個實現EmailNotify比預期的要困難得多,因爲我驚訝地發現MailMessage不是可序列化的,而是讓它發生了。在N層架構中實現數據庫功能對象?

現在我工作的一個新的具體通知,DBNotify,它將調用某種形式的SP和更新的主要事務數據庫狀態。我很沮喪,因爲我想重用我們已經創建的DAL架構,但是INotify是Model項目的一員,它比DAL更爲基礎。

我們的層次看起來是這樣的: 通用>模式> DAL> BAL

以下是關於層的更多細節。請記住,我繼承了這個: 通用負責所有在應用程序中使用很多地方的「實用程序」功能,例如訪問配置設置,解析字符串和與業務無關的功能。

模型是業務對象,有些人稱之爲數據傳輸對象,getter和setters集合。我在這一層添加了一些「智能」,但只有該對象內部的業務規則,例如「一個項目的名稱必須以字母數字字符開頭」。

DAL是數據訪問層,理論上這裏發生的所有事情都是模型對象被移入和移出數據庫。

BAL是業務層;從理論上講,管理對象交互的業務規則是強制執行的(即「一個表單必須至少有兩個項目」)。

所以inotify的接口被定義的抽象,以允許通知的方法獨立地改變(即電子郵件,TXT,微博等)。這是系統的基礎,所以我在模型層創建了它,它獨立於DAL層。但是,我正在創建一個新的具體實現INotify,其通知方法是調用數據庫中的SP。

是否有其他人處理過其目的是與數據庫交互的業務對象,以及您如何將其置於N層架構中?

在你告訴我使用Linq to Sql之前,非常感謝。這不是一個技術問題(我怎麼做),這是一個設計問題(我應該怎麼做)。

我覺得有一個StackExchange網站上這些種類的語言無關性設計問題更集中,所以我要去那裏複製此。

+0

*接口只是名義上的,因爲其實我是想序列化這些對象,我不得不做出它一個抽象類。 – 2011-03-30 19:03:20

+1

我對你的層次感到困惑。模型和BAL負責什麼?我拿BAL來代表商務訪問層,但那個模型看起來不合適。在代碼中,我看到模型通常是最抽象的,位於上面(使用)或是業務層的一部分......另外,如果模型比DAL更基礎,那麼DAL使用類的問題是什麼從模型中調用方法? – 2011-03-31 07:23:29

+0

哦,如果你不知道,你不需要在評論中添加信息,你可以簡單地編輯你的問題... – 2011-03-31 07:24:05

回答

0

學到了很多東西謝謝大家對你的輸入,在這裏爲我改善計劃實施有幾個想法,雖然沒有直接回答我提出的問題。

我交叉發佈給程序員,我認爲這樣的問題可能真的屬於,並得到了一些有用的想法。如果你有興趣,線程在這裏: Programmers thread on this issue。無可否認,當我在那裏發佈時,我在自己的研究基礎上添加了依賴注入的「提示」,所以問題可能會更清楚。

這是一個偉大的和有益的社會,我很自豪參加。

1

如果你的模型類是你的DTO(有些什麼可以稱之爲數據結構或數據類型),他們應該(可能)外行「跨越」的其他層和是已知的所有的人。

根據您的說法,您可能有一個位於BAL中的MessageProcessing類,並接收來自BAL或DAL其他部分的消息,然後通知正在監聽的任何人(BAL或其他感興趣的BAL成員)。

+0

消息進入MSMQ並由作爲服務運行的代碼進行處理,因此在Web應用程序內創建MessageProcessing類不會有幫助。但你的回答給了我一個主意。 – 2011-04-01 14:58:38

2

僅僅因爲你的INotify接口是在模型層,並不意味着所有具體的實現需要在那裏。它應該是一個接口 - 接口的目的是實現抽象 - 而不是基類 - 基類的目的是實現共享功能。因此,任何你在任何圖層中都有這種類型的屬性或參數的應用程序,應該聲明爲INotify。在您的BAL中(您的意思是BLL,業務邏輯層?),您將決定用於INotify實例的具體類型。根據您的通知的複雜程度,您可以在BLL中定義具體的實現,並讓它在DAL中使用助手類實際執行對您的sproc的調用,或者您可以直接在DAL中將它定義爲類,因爲它與數據庫交互;這實際上是根據班級負責的程度進行的判斷。無論哪種方式,它應該可以在頂層訪問。

是否有其他人處理過其目的是與數據庫交互的業務對象,以及如何將其置於N層架構中?

你的項目是結構化的方式,聽起來好像每一層的邏輯職責是:

常見: 在項目
型號別的共享的實用方法,無依賴性: 定義系統中實體的結構,也稱爲DTO或數據傳輸對象,這意味着它們可以在層之間傳輸。他們所做的只是存儲數據並執行基本驗證。
DAL: 負責從您的模型層創建類的實例,並根據存儲在存儲庫(如數據庫)中的值設置屬性。還負責跟蹤對模型實體的更改,並將這些更改保存(保留)回存儲庫。
BAL/BLL: 使用其他圖層中定義的類來實現一些有用的內容,並驗證是否遵循了業務需求。

您可以通過各種技術實現這一目標,甚至使用相同的技術,您的確切實施將因您的工作方式而異。像Linq2Sql或開箱即用的Entity Framework會模糊您的模型和您的DAL之間的界限;他們想要在同一個項目中定義兩者。但是,實體框架更加靈活,通過一些工作,您可以將實體模型的定義和最終負責DAL組件的「上下文」(使用實體框架術語)分解爲單獨的項目。您可以編輯T4模板或在線查找那些將從實體模型生成實體和上下文類定義的模板,以支持Repository和Unit of Work設計模式,其中您從不直接引用實體上下文,而是實現IRepository接口,這使得你的代碼更加可測試。我從來沒有親自與NHibernate合作過,但我的理解是它有能力做同樣的事情(可以說目前可以做得更好)。

+0

不錯的努力,這裏有一個建議,雖然:在SO中的答案往往是短而重要的。要麼嘗試縮短它,要麼將它分成多段並添加標題。 – jgauffin 2011-04-01 06:41:54

+0

INotify在功能上是一個接口,但是你不能序列化一個接口,所以我把它變成了一個抽象類。它限制了靈活性(如果它是一個接口,我可以繼承其他類並仍然實現INotify)。 – 2011-04-01 14:52:48

+0

是的,BAL == BLL。只是使用我繼承的名字。 – 2011-04-01 14:55:31

3

也許不是你的問題的答案,但有些事情要考慮。

我不同意將組件層次結構中的數據訪問權限。我不會把它放在兩個功能域圖層之間。甚至不在單個域模型類的「上方」。數據訪問或持久性不是任何域類的問題。它只應該是對他們可以做的事情,而不是他們做的事情。

即使我開始編碼像TClient.SaveTClient.Load之類的東西,我現在已經得出結論,決定需要保存的不是客戶端,而是需要用戶交互來指示何時需要域實例的數據,因此應該被加載,並且當一個客戶的數據應該被保存時,如果是的話。因此,我現在是編碼的支持者(在GUI中,更具體地說是GUI中的控制器),例如DataStore.Load(ClientInstance)DataStore.Save(ClientInstance)。然後由數據訪問層決定如何做到這一點。它可以在C#中使用反射,或者在Delphi中使用新的RTTI來遍歷所有客戶端的屬性,以便將它們發送到數據庫。

儘管分層是一個非常好的概念,可以分散顧慮,並且通過簡單地堅持「可以調用但不能調用」來解決問題,但是在解決日誌記錄,異常處理,通知以及其他所有組件/層需要的其他有趣的交叉切割問題。

此外,公共層,因爲它是一個實用程序層,所以其他層應該可以訪問。

爲了把它們都放在一個畫面(這裏,我讓你做簡單的領域類,你的模型,並跨類業務規則,您的BAL之間的區別):

+---+ +-------------+ 
| C |<--| Data Access |<--------------------------+ 
| o | +-------------+       | 
| m |   |         | 
| m |   |         | 
| o |   v         | 
| n | +-------------+ +----------------+ +-----+ 
| |<--| Model  +<--| Cross class |<--| GUI | 
| | +-------------+ | business rules | |  | 
| |      |    | |  | 
| |<--------------------|    | |  | 
| |      +----------------+ |  | 
| |           |  | 
| |<-----------------------------------------|  | 
+---+           +-----+ 

inotify的實現調用進入數據庫當前是在模型中,它在上面的圖片中並沒有調用到數據訪問層本身,它只是被數據訪問層調用,或者說是詢問。

問題的關鍵在於INotify是否應該位於「模型」中,作爲領域層的一部分,還是應該是一個通用接口,應該有一個單獨的「通知」層/組件可以從域和GUI。這個新組件不僅可以關注通知,還可以關注許多其他交叉問題,例如日誌記錄。它至少以某種回調方式訪問公共(當然)和數據訪問組件以及GUI。

在下面的圖片中,我試圖想象這一點,但我並不擅長可視化,並總是與那些討厭的交叉刀具有問題。這就是爲什麼沒有從域層到橫切關注的呼叫箭頭,儘管域層應該能夠訪問例如「記錄器」界面。也許我試圖很難區分常用組件和交叉組件,並且可以將這些組件放在一起,並將它們可視化爲「實用程序」層/組件中的單獨塊。

 +--------------------------------------------+ 
    +-----| Cross cutting concerns      | 
    |  +--------------------------------------------+ 
    v   v^         ^
+---+ +-------------+        | 
| C |<--| Data Access |<--------------------------+ | 
| o | +-------------+       | | 
| m |   |         | | 
| m |   |         | | 
| o |   v         | v 
| n | +-------------+ +----------------+ +-----+ 
| |<--| Model  +<--| Cross class |<--| GUI | 
| | +-------------+ | business rules | |  | 
| |      |    | |  | 
| |<--------------------|    | |  | 
| |      +----------------+ |  | 
| |           |  | 
| |<-----------------------------------------|  | 
+---+           +-----+ 
1

如果您的項目是POCO,您可以在您的項目中使用您的數據實體。否則,我會像你所做的那樣創建單獨的模型。但是,請將它們保存在單獨的程序集中(不在DataAccess項目中)

imho人過度使用圖層。大多數應用程序不需要很多層。我目前的客戶有一個像您的應用程序一樣的架構。問題是隻有數據訪問層和表示層在其中存在邏輯,所有其他層僅從下層獲取數據,對其進行轉換並將其發送到上面的層。

我做的第一件事就是告訴他們放棄所有層,而使用這樣的事情(需要IoC容器):(通過一個ORM包含業務規則和數據訪問)

  • 核心
  • 規格(分隔接口模式。包含的服務接口和模型)
  • 用戶界面(可能是一個Web服務,的WinForms,Web應用程序)

,對於大多數申請工作ication。如果您發現核心增長並變得太大,您可以分割它而不影響任何用戶界面。

您已經在使用ORM,並且您是否考慮過使用驗證塊(FluentValidation或DataAnnotations)進行驗證?使您可以輕鬆驗證所有圖層中的模型。

0

許多層次中使用的類讓我擔心。

尤其是當它們也綁定到數據模型/基礎/圖層時。

只要這些類發生變化,您就可以在所有圖層中進行重新編碼。換句話說,你錯過了抽象的有益效果。這就是說,保持轉換代碼(從一層到另一層)並不是很有趣,但總的來說工作量較少。

一個介於兩者之間的解決方案可能是使用接口/角色:爲每個層定義一個對象應該播放的接口/角色,並使用該接口傳遞給該層。然後一個(共享的)類應該實現一個角色(或者其中的許多角色)。這將提供更鬆散耦合的系統。

我從this neat lecture about DCI (Data, Collaborations, and Interactions)