.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負責修復模型狀態、執行驗證和處理樂觀併發。app

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。this

備註

只看代碼你們可能以爲A方案比較簡單,而B方案視乎有點脫褲子放屁的感受,我以前都是用的A方案,開發效率確實高,可是應對比較複雜的邏輯就很是不爽了,具體爲啥不爽我尚未想明白。加密

我如今很是有信心用好任何一個方案,由於一個高人告訴我:關注代碼細節勝於關注這些架構上的問題。spa

結合四色原型,我以爲能夠這樣弄:PPT和DES用A方案,MI用B方案。code

相關文章
相關標籤/搜索