.NET應用架構設計—面向查詢服務的引數化查詢設計(分解業務點,單獨配置各自的資料查詢契約)

王清培發表於2014-02-06

閱讀目錄:

  • 1.背景介紹
  • 2.對業務功能點進行邏輯劃分(如:A、B、C分別三個業務點)
    • 2.1.配置對映關係,對業務點配置查詢契約(構造VS外掛方便生成查詢契約)
    • 2.2.將配置好的對映策略檔案放在呼叫端,與服務不耦合
  • 3.Dynamic、Dom動態構造服務端物件(Dynamic、DOM實現動態DOM)

1.背景介紹

現在越來越多的公司都在嘗試SOA架構的實踐,本人最近也在嘗試學習這方面的技術,但是在實踐過程中遇到一個問題,我想這個問題也是我們普遍實踐者都應該會遇到的問題,問題描述如下:

我們有一個SOA商品(Item)查詢介面,這個介面很通用,主要用來支撐日常很多其他系統的大量關於Item的查詢,尤其是在高峰期間該服務的壓力是很大的;我們站在SOA的角度看這個介面,這個通用的介面解決了眾多的查詢業務,確實不錯,但是我們切換一下角度,站在每一個呼叫介面的訪問端看似乎並不是很滿意或者說犧牲了部分效能上的代價,因為我們無法乾淨利落的只獲取當前這個業務點需要的資料項;這個Item服務介面所返回的資料項必須同時滿足所有呼叫它的業務點,哪怕這次呼叫我只需要用到Item的三分之一的資料欄位都不行,每次都會把不需要的欄位都查詢出來,不管是返回的效能、查詢的效能,其實都是可以通過調整設計來避免的;

(檢視大圖)

以往我們的思路都是集中在服務端,常規做法都是提供了一個能夠容納所有查詢客戶端需求的資料實體,客戶端可選擇的餘地很有限,無法只獲取自己所需要的幾個資料項,甚至各個業務點在不同的情況下都有可能需要兩到三個資料返回實體;總而言之,面向資料查詢的服務介面如果要向著SOA方向發展那就必須包含SOA設計上的相關原則,如這裡的面向查詢為主的服務設計其實就是缺少SOA原則中的”服務應具有策略性“一原則;

為什麼以往一直沒有暴露出這個問題呢,是因為以往都是在本地直接呼叫“查詢引擎”,如:SQLSERVER,在“查詢引擎”的最後一層就是應用程式,而應用程式中可以編寫很多彼此類似的查詢方法,每個方法可能只有一兩個欄位的差異性,或者通過“企業應用架構模式—查詢物件模式”來將不同的方法合在一起通過一個可以調整查詢欄位的物件來配置本次需要的查詢欄位;由於現在我們已將查詢服務化,就不太可能再去為了所有客戶端在去適應性的去擴充類似沒有太大價值的介面,但是客戶端又需要將自己所需要的查詢欄位讓服務知道,所以這裡的解決方案可以稱為面向SOA的”企業應用架構模式—查詢物件模式“

本文將通過運用”關注點分離“通用設計思想來對查詢服務在服務端的強耦合進行分解,將強耦合從服務端遷移出來通過策略性的配置將關注點放入各自的客戶端,從而有效的解決服務不再臃腫的問題,如果理解上有困難可以嘗試使用面向SOA的”企業應用架構模式—查詢物件模式“概念來理解;

2.對業務功能點進行邏輯劃分(如:A、B、C分別三個業務點)

首先我們需要將相對於服務來說的客戶端中所有業務點進行邏輯劃分,將原本一個高耦合的龐大資料實體分解成各自所需要的一個精簡的資料實體;業務點的劃分目地在於可以將資料實體能與之對應起來,這個資料實體是針對於查詢服務而言的,對於客戶端來說沒有任何的依賴和約束,也就是說本次業務點發起的查詢將把這個資料實體轉化成一組查詢策略中的設定帶到服務端中,然後服務端在根據這組策略資訊進行組合最終的查詢語句;

注:這裡的資料實體並不是服務端定義的DTO,也不是客戶端定義的DTO,而是一個只跟本次業務查詢相關的資料查詢實體,該實體不是一個定義的類,而是一個策略,類似:

A.Business{Query field{ItemNumber、Description、PromationPrice}}

這樣一組配置資訊;客戶端用來反序列化的DTO可以是一個龐大的共用的資料實體,也可以是跟業務點繫結的精簡實體,對於查詢沒有任何影響,我們要解決的是“只查詢我所需要的資料項,只返回我所需要的資料項”,而跟你在服務端、客戶端定義的用來輔助序列化的實體沒有任何關係;

 (檢視大圖)

將查詢的欄位、返回的欄位通過查詢策略帶入到服務端,我們就能夠知道本次業務點查詢的是需要什麼樣的欄位,然後就可以在構造查詢引擎引數時將返回的欄位直接加上或者過濾不需要的;

2.1.配置對映關係,對業務點配置查詢契約(構造VS外掛方便生成查詢契約)

將系統中需要呼叫服務介面的所有功能點進行業務點邏輯劃分設計後,每個業務點都需要在自己發起呼叫服務的時候能夠帶上在之前某個時間點設計好的查詢契約,這個用來生成查詢契約的工具最好是整合在VisualStudio中的自定義外掛,在設計時用來動態構造一個對應的契約配置檔案,如果可以的話可以採用動態程式碼方案,將配置檔案的靜態檔案通過動態生成程式碼的方式嵌入到生成的程式碼中去,減少不需要的配置檔案,也減少查詢框架的效能開銷,一次生成後就可以直接使用;

2.2.將配置好的對映策略檔案放在呼叫端,與服務不耦合

本篇文章的解決方案最大的突破點就是將關注點從服務端轉移到所有客戶端上,將原本都集中在服務上的所有客戶端的需求分離出去,每個需要呼叫服務的客戶端維護好自己的一份查詢契約,在每次呼叫服務的時候帶上那份契約;如果處於效能考慮每次帶上契約會增加開銷,那麼可以在第一次身份驗證的時候在服務上確認,以後呼叫都忽略;

這樣一來服務將精簡很多,通過同樣的設計方法可以用來設計很多類似的服務介面,將關注點從服務上轉移到客戶端上,會是一個很好的設計思路;

3.Dynamic、Dom動態構造服務端物件(Dynamic、DOM實現動態DOM)

藉助C#新特性Dynamic,我們可以在.NET平臺上進行動態程式設計,這裡可以解決我們預先定義服務端實體的好處;以往我們需要在服務上定義一個至少能容納所有客戶端查詢契約中的所有資料項的實體,但是當我們運用動態程式設計時,我們無需事先定義一個類,而可以在執行時動態獲取物件屬性,當然這得益於.NETDLR的實現;再適當的結合DOM思想,我們就可以實現一個動態DOM效果,對於DOM的某個Element的訪問也無需定義對映實體然後在通過屬性獲取,中間既增加了序列化的開銷還增加了開發工作量;

 1 using System;
 2 using System.Collections.Generic; 
 3 
 4 namespace ConsoleApplication1.DynamicDom
 5 {
 6     public class ItemEntity : System.Dynamic.DynamicObject
 7     {
 8         /// <summary>
 9         /// 可以使用LINQ TO XML或者將XML資料構造在一個指定的容器中,用來判斷是否存在
10         /// </summary>
11         private static Dictionary<string, object> itemPropery = new Dictionary<string, object>();
12         static ItemEntity()
13         {
14             itemPropery.Add("ItemNumber", 1029394);
15             itemPropery.Add("PromationPrice", 100);
16         }
17         public override bool TryGetMember(System.Dynamic.GetMemberBinder binder, out object result)
18         {
19             if (itemPropery.ContainsKey(binder.Name))
20             {
21                 result = itemPropery[binder.Name];
22                 return true;
23             }
24             result = null;
25             return false;
26         }
27     }
28 } 
1 dynamic itemEntity = new DynamicDom.ItemEntity();
2 Console.WriteLine("ItemNumber:" + itemEntity.ItemNumber);
3 Console.WriteLine("PromationPrice:" + itemEntity.PromationPrice);
4 Console.ReadLine(); 

這裡只是一個簡單的示例,目的是想讓並不太瞭解Dynamic的朋友有了直觀上的認識;通過使用Dynamic、Dom可以在服務端上無需定義任何的實體,根據各個客戶端傳過來的配置直接進行動態訪問,可以藉助LIQN TO XML;

全文僅僅是一個設計上的介紹,要想完全實現上面這些效果需要還是需要開發些東西的,這裡只是拋磚引玉,希望對正在設計相關內容的朋友提供一個思路;

相關文章