.NET:關於數據模型、領域模型和視圖模型的一些思考


背景

數據模型、領域模型和視圖模型是“模型”的三種角色,一些架構用一種類型表示這三種角色,如:傳統三層架構。也有一些架構用兩種類型表示這三種角色,如:結合ORM的領域驅動架構。非常少見的場景是用三種類型表示這三種角色,我只在個別領域這么弄過,如:工作流引擎。

今天只說一個話題:是否有必要為視圖模型引入獨立的類型?還是用一種類型表達領域模型和視圖模型這兩種角色比較方便?引入一些詞匯:

  • A方案:用一種類型表達領域模型和視圖模型這兩種角色,又叫公開領域模型到視圖(Open Domain To View)。
  • B方案:為視圖模型引入獨立的類型,又叫使用數據傳輸對象(DTO)。

A方案

因為領域模型和視圖模型是一個類型,所以領域模型會從UI進行重建,因為領域模型會從UI進行重建,而UI層是不能相信的,所以必須對領域模型進行驗證(包含IsValid()方法),而且領域的很多方法都是修復領域模型的非法狀態,如:重新計算訂單總額、加密未加密的密碼屬性等等。

代碼示例

 1     internal sealed class TestGridCommandHandler : ApplicationService,
 2         ICommandHandler<CreateTestGrid>,
 3         ICommandHandler<UpdateTestGrid>,
 4         ICommandHandler<DeleteTestGrid>
 5     {
 6         public void Handle(CreateTestGrid command)
 7         {
 8             var testGridService = this.Service<TestGridService>();
 9 
10             testGridService.Create(command.TestGridInfo);
11             command.Id = command.TestGridInfo.Id;
12         }
13 
14         public void Handle(UpdateTestGrid command)
15         {
16             var testGridService = this.Service<TestGridService>();
17 
18             testGridService.Update(command.TestGridInfo);
19         }
20 
21         public void Handle(DeleteTestGrid command)
22         {
23             this.Service<TestGridService>().Delete(command.Id);
24         }
25     }

注意看第二個方法,這里的command.TestGridInfo就是領域模型,從客戶端重建后直接調用ApplicationService進行update,update負責修復模型狀態、執行驗證和處理樂觀並發。

B方案

因為領域模型和視圖模型是一個不同的類型,所以領域模型不會從UI進行重建,因為UI進行重建的只是視圖模型, 所以要從數據庫加載一份領域模型,然后將視圖模型合並到領域模型中,這里的合並不是指用AutoMapper這樣的合並工具,而是一種合理的合並過程(不能用反射繞過領域模型封裝的邏輯),在這個合並過程,領域模型始終處於合法狀態(也可以不合法,很多人都這么弄,保留IsValid()方法即可)。

代碼示例

 1     internal sealed class TestOrderCommandHandler : ApplicationService,
 2         ICommandHandler<CreateTestOrder>,
 3         ICommandHandler<UpdateTestOrder>,
 4         ICommandHandler<DeleteTestOrder>
 5     {
 6         public void Handle(CreateTestOrder command)
 7         {
 8             var testOrderService = this.Service<TestOrderService>();
 9 
10             var testOrder = command.CreateTestOrder();
11 
12             testOrderService.Create(testOrder);
13             command.Id = testOrder.Id;
14         }
15 
16         public void Handle(UpdateTestOrder command)
17         {
18             var testOrderService = this.Service<TestOrderService>();
19 
20             var testOrder = testOrderService.Repository.Load(command.Id);
21             testOrder.CheckOptimisticKey(command.TestOrderInfo.OptimisticKey);
22             
23             command.UpdateTestOrder(testOrder);
24             testOrderService.Update(testOrder);
25         }
26 
27         public void Handle(DeleteTestOrder command)
28         {
29             this.Service<TestOrderService>().Delete(command.Id);
30         }
31     }

注意看第二個方法,這里先用Repository從數據庫返回一個領域模型,執行樂觀鎖檢查,用視圖模型修改領域模型(不是簡單的反射),然后調用ApplicationService進行Update。

備注

只看代碼大家可能覺得A方案比較簡單,而B方案視乎有點脫褲子放屁的感覺,我之前都是用的A方案,開發效率確實高,但是應對比較復雜的邏輯就非常不爽了,具體為啥不爽我還沒有想明白。

我現在非常有信心用好任何一個方案,因為一個高人告訴我:關注代碼細節勝於關注這些架構上的問題。

結合四色原型,我覺得可以這樣弄:PPT和DES用A方案,MI用B方案。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM