2011-05-02 62 views
4

我正在開發一個3層應用程序(不是3層!),其中一個層(物理羣集)上運行的客戶端應用程序與另一個服務應用程序層和另一層上的數據庫服務器。應用程序有很多業務規則,流程邏輯等,我相信應該可以在應用程序層和服務層上使用,以改善用戶體驗,減少對服務的調用並消除冗餘編碼。.NET中的分佈式DDD:與客戶端共享域對象

讓我們用這個例子:在我的領域層,我有一個Document對象。該對象包含AllowPublish屬性,該屬性檢查對象的內部狀態,如果狀態允許發佈文檔,則返回true/false。該對象還具有一個Publish方法,該方法通過將IsPublished標誌設置爲true並引發已發佈的域事件來修改對象的內部狀態,以反映它正在發佈的事實。

我有一個單獨的AuthorizationService,它確定是否允許當前用戶發佈以及將該對象持久化到數據庫的DocumentRepository。

在我的服務應用程序中,我的DocumentService有一個PublishDocument方法,它接受文檔ID,使用ID從存儲庫中檢索文檔,檢查AllowPublish屬性,如果爲true,則調用Publish然後使用存儲庫持久更新的對象。

我在客戶端上的行爲稍有不同。在這種情況下,我使用AllowPublish屬性來啓用/禁用命令按鈕。啓用並單擊後,我會調用一個服務代理,該服務代理公開一個接受文檔ID的PublishDocument方法。代理將該調用傳遞到具有相同名稱的服務應用程序的DocumentService方法。爲了消除重複的代碼,共享業務邏輯,驗證規則等,我將域對象放置在一個單獨的程序集中,由客戶端應用程序和服務應用程序共享。這意味着客戶端應用程序現在可以訪問我的Document類的Publish方法,儘管它只與我的服務應用程序有關,並且只應該被我的服務應用程序使用。這讓我重新考慮了我正在採取的整個方法。

雖然我瞭解使用DTO在客戶端和服務器之間傳遞狀態,但我使用.NET 3.5,據我所知,共享程序集是共享業務和驗證規則的唯一方法。客戶端應用。我有一些想法,我可以去的其他方向,但希望得到一些建議,然後開始一條新的道路。

另一方面,我目前對客戶的實施採取了我認爲是一種全面的授權方法,可能只是一個指標,表明不同的模式會更好。就像我在我的服務器端服務應用程序中有一個DocumentService用來執行授權的AuthorizationService,我有一個類似的代理,我的客戶端代碼使用它。這意味着我需要在我的客戶端代碼中使用另一個間接層來支持授權,可能是Controller或ViewModel。如果用例是有效的,那很好。

EDIT

我可能需要澄清的是,AllowPublish屬性是動態的文檔正在被編輯時。當第一次檢索時,它可能是錯誤的,但隨着業務規則的滿足會變成真。通過在客戶端應用程序中運行業務規則,我們可以提供更豐富的用戶體驗。

+2

這可能是調查CQRS的好時機。這不是一小步,而是根據你所描述的,這可能是一個很好的選擇。這個想法是爲寫入(命令)和讀取(查詢)分別建立模型。這樣,您的文檔對象(又名DDD實體或Agregate)將成爲寫模型的一部分,在讀模型中,您將擁有一個簡單的DTO,其中包含所有已經計算好的屬性 - DocumentDTO的AllowPublish將成爲一個簡單的布爾字段每當Document實體發生更改時都會更新。如果您需要我可以提供更多信息,但我認爲谷歌應該足夠了。 – 2011-05-02 14:18:12

+0

事實上,我的精神實施CQRS。例如,檢索文檔列表的查詢方法實際上會返回包含簡單隻讀屬性(不過是DTO)的DocumentInfo對象。這些DTO仍然由具有邏輯的實際域對象生成,以確定像AllowPublish這樣的布爾屬性將包含哪些值。雖然我喜歡CQRS背後的概念,但我並不喜歡實現,因爲我發現它超出了平均開發人員的理解範圍。所以,我嘗試用R/O DTOs來簡化讀取和寫入域對象。 – SonOfPirate 2011-05-02 15:34:13

+0

我應該澄清:CQRS因爲它是一個交易存儲而失去了我 - 這意味着我們正在存儲交易,而不是狀態。這對許多應用程序來說都很好,但對於我的努力,我還沒有遇到過這樣的情況,因爲它非常適合。我不想通過一系列交易來檢索我的當前狀態,並且實施服務對於彙總數據的額外開銷等等太多。我喜歡分離職責的想法,實際上已經實現了一個版本,其中查詢是針對CUBE而不是實際的數據存儲執行的。這跟我來的時間差不多。 – SonOfPirate 2011-05-02 17:11:00

回答

3

你不應該把你的域模型對象放在客戶端。讓他們直接在客戶端使用會限制您在將來的迭代中演進域的能力,並且在進行DDD時,當您從領域專家那裏獲得更深入的洞察力時,能夠演進您的域是至關重要的。

我不知道在你的情況下這是否可能,但也許你可以將業務規則分解爲一些策略對象,這些策略對象只具有非常特定的行爲,既可以在域模型中使用,也可以在客戶。如果您的目標是避免邏輯重複,並且您需要的行爲完全相同 - 這可能並非如此。在您的客戶端中,您可能需要一些額外的驗證步驟,這可能與您在域模型中需要的步驟不同。

如果可能的話,基於某些共享規則,可能最好的解決方案是使用MVC或MVVM模式,當您可以在ViewModel中進行客戶端驗證時。

我客人的主要想法是爲了DRY而不結合概念。像往常一樣,烏迪大漢作爲此文章:The Fallacy Of ReUse

+0

我會+1您參考Udi Dahan的文章,但我不同意域模型對象不應該在客戶端內使用的想法。在某些系統中,讓域名可以在本地訪問是更有意義的,以減少通過服務器調用服務器的必要性,例如「請驗證此單個字段?」。或「根據具體投入計算新的最終價格」。 – jpierson 2011-08-16 20:04:42

+0

與軟件開發中的任何東西一樣,沒有黑色和白色。作爲指導,您不應該在客戶端使用域模型對象。在實踐中,我確信有些情況下,在客戶端使用域模型對象是可以接受的 - 但只有在瞭解採用此路徑的全部含義後,才應該這樣做。 – 2011-08-17 09:48:38

+0

在客戶端沒有域模型背後的原因是什麼?域的安全/完整性?我可能是錯的,但我希望有許多應用程序存在於域僅存在於客戶端的地方。除非我們談論的是一個Web應用程序,否則我認爲將這個域獨佔於N層架構中的服務器或中間層是沒有好處的。雖然會對你的想法感興趣。 – jpierson 2011-08-17 19:02:06

0

考慮使用InternalsVisibleTo屬性。

+0

這是我正在考慮的選項之一,但我通常保留授予單元測試訪問權限。我有這樣的感覺,這可能會使代碼有點「臭」。但是,這是我想到的一個考慮因素。 – SonOfPirate 2011-05-02 15:35:22

4

關閉和任何人的緣故是遇到這個職位在未來,我想我會分享什麼我結束了,同時給予信貸尤利安幫助轉向我在正確的方向。

簡而言之,我意識到(在Iulian的幫助下)我確實在客戶端應用程序和服務器端服務應用程序之間有兩種不同的用例。結果,我咬了嘴脣,爲每個人創造了獨立的領域模型。

我思考過程的一部分是邏輯分離應用程序本身。雖然客戶端應用程序無法在沒有服務應用程序的情況下運行,但我調整了思路,將這種關係更多地視爲數據訪問層,而不是應用程序層和領域層。同時,我將自己的觀點轉移到服務器端,將服務接口看作該應用程序的表示層。因此,擁有不同的域對象/層是非常有意義的。

不幸的是,這確實帶來了一個折衷。我希望在推進技術發展時儘量減少利用RIA服務,這可以讓我們將數據註釋從服務器傳遞到客戶端。但是,現在我正在使用簡單的DTO對象在應用程序和提供API的核心域邏輯的RESTful服務接口之間傳遞狀態信息。

使用我原先所列舉的例子,我有以下設置:

當用戶單擊界面中的「發佈」按鈕,在客戶端應用程序調用我的服務代理類發佈方法。服務代理處理與服務器端服務的通信。在這種情況下,DocumentService公開一種發佈方法,該方法接受要發佈的文檔的ID(以及用戶信息等)

DocumentService從DocumentRepository中檢索Document域對象,並調用Publish方法更新Document的內部狀態的對象。該服務然後調用DocumentRepository上的Update方法,傳遞更新的Document對象,並將更改持久化到數據庫。

折衷是我需要有邏輯/規則來決定是否以及何時在客戶端和服務器上發佈Document(因爲我們不能假定請求總是有效的)。同樣,將解決方案作爲兩個獨立的應用程序以及它們自己的一組用例幫助使這更加合理(在我看來)。如您所見,我不需要客戶端版本的Document中的Publish方法,但我確實需要更改跟蹤才能獲得豐富的用戶體驗。我不需要在服務器上進行相同類型的更改跟蹤,因爲在更改對象時不會刷新UI。在後一種情況下,更改跟蹤由ORM實施,以使持久性更優化。

因此,對我來說最重要的是將解決方案分成不同的應用程序,這使我能夠隔離用例並繪製出每個應用程序所需的適當對象和關係,以滿足其目的。現在我有一個解決方案,它也支持多個客戶端,因爲我已經將客戶端與服務器分離開來,使我可以插入新的客戶端,而無需更改服務應用程序或現有的客戶端應用程序。

HTH

相關問題