Core-Man System Design Document
Version 1.0
9/29/2009
Contents
簡介 4
系統總體架構介紹 4
系統具體設計 6
后記 6
簡介
本文檔用來描述Core-Man這個用來為汽配行業做貨物、訂單、客戶關系管理的系統功能和設計。本文檔基于該系統的需求分析和前期市場調研,關于需求分析和市場調研的結果,可以查看文檔XXXX.
1. 系統設計目標
為了使得系統能最大程度的滿足用戶的需求,盡可能的減少以后再定制維護過程中的代價,Core-Man系統應該達到以下幾個目標:
? 可定制性:雖然本系統是為了汽配行業的客戶管理而設計的,但是本系統需要達到在特定的需求下經過適當的配置(數據庫配置,配置文件更改)而達到滿足其他行業需求的目的。
? 可維護性:本系統的設計應該達到可維護的目的,因為用戶的需求可能會變化(企業規模擴大,經營業務增多)。在用戶變化需求的時候,我們的系統就需要升級,那么在升級的過程,至少需要滿足兩方面的可重用,一是數據上的可重用??蛻粼谝欢ǖ慕洜I過程中積累了一定數目的用戶資源和經營經驗數據,這部分數據對于客戶以后繼續經營,擴大業務規模都是有重要作用的。在我們的系統升級的過程,應該使得客戶可以重用這部分數據,即使得升級后的系統能夠識別維護舊的數據。另一方面的可重用性是指代碼上的可重用性,系統的升級不能使推到系統重來。新系統應該基于舊系統已有的代碼,這樣可以最大程度上的節省成本。
? 安全性:雖然在系統初期,客戶對于安全性上的要求不會很高,但是隨著企業業務規模的擴大,必然涉及到很多的商業保密信息。企業的客戶數據,訂單數據,管理模式都有很重要的商業價值,此時對于我們系統的安全性就提出了要求。Core-Man系統的安全性主要體現在一下幾個方面:
? 用戶認證:系統應該區分登錄的會話中用戶的身份,以此來判斷用戶可訪問哪些數據,可更改哪些數據,這個對于一個中大型企業里面,各個管理層面(老板,總經理,經理,員工)應用同一個系統式很重要的。
? 權限管理:驗證用戶后,需要判斷已驗證用戶是否被允許訪問其所請求的數據,在判斷通過的情況下,系統返回正確的響應;否則,系統告知客戶端無權訪問。
? 數據加密:企業的客戶數據,員工數據,訂單數據都涉及到商業秘密。這些數據都需要經過加密后存儲在系統的文件系統或者數據庫中。
系統總體架構介紹
很明顯的Core-Man系統是一個以數據庫為中心,以多終端進行數據處理維護的分布式系統,一個Core-Man系統的典型的應用場景可以用下圖來描述:
在上面的應用場景中,各個角色的分工如下:
? 中心數據庫:負責提供整個系統的數據的存貯,這里包含兩部分內容
? 由數據庫管理系統托管的數據庫表,視圖,存儲過程,數據等信息。
? 由文件系統托管的圖片,文件等信息。
? 請求監聽/代理服務器:負責作為終端用戶和數據庫之間通信的一個代理人的角色。所有客戶端的操作如果涉及到數據庫查詢/更改請求,由請求監聽/代理服務器將其轉換為對相應數據庫項目的查詢/更新操作。這樣的設計能使得終端用戶和數據庫的耦合性降到最低,使得在需要更換數據庫的時候,我們只需要更新請求監聽/代理服務器里面與數據庫交互的那部分代碼。同時,加進一層請求監聽/代理服務器,可以讓我們的系統在這一層進行安全性上的檢查更改等操作,比如,在這一層做終端用戶身份的鑒別,只有終端用戶是授權用戶的時候,我們才允許客戶端進行后續操作。
? 超級管理終端:這個終端有最大的管理權限,用于進行數據的備份,緊急模式下的更改等操作。
? 分布式終端1:本系統眾多分布式終端的其中一個,用戶為普通用戶(經理,員工,客戶)提供訪問我們系統的功能。每一個分布式終端在發起連接請求監聽/代理服務器的時候,都需要提供用戶身份的信息,請求監聽/代理服務器用這些信息來判斷發起連接的用戶是否是一個經過授權的用戶。
下圖描述了我們的數據庫設計的基本輪廓:
下圖描述了我們的請求監聽/代理服務器的設計模塊以及模塊之間的聯系:
自上至下,三個大的模塊分別是:
? 請求監聽層:負責監聽客戶端的操作請求,檢查客戶端的身份,以及客戶端的權限。如果客戶端是經授權的用戶并且其請求操作被策略允許,請求監聽層會將該請求放進該層維護的請求隊列中。放進請求隊列中的請求包含請求的客戶端信息,請求的優先級,請求所要進行的具體操作等等。請求監聽層會有一個請求分發線程,從請求隊列中按照一定的策略找出一個請求,將該請求送往邏輯處理層。
? 邏輯處理層:邏輯處理層的主要功能是維護抽象的數據模型,使得請求層不必與具體的數據庫訪問器接口方法做交互。邏輯處理層將請求監聽層分發過來的請求轉換為對特定抽象數據的操作,這就使得在這一層能盡最大可能地應用面向對象設計的好處,為以后的升級,定制提供方便。邏輯處理層由請求監聽模塊,邏輯處理模塊和邏輯對象管理器組成。請求監聽模塊將請求監聽層分發過來的請求轉換為對特定邏輯對象的操作。邏輯處理模塊是一個特定的線程,負責調用邏輯對象管理器的方法來進行邏輯多項生命周期的維護。邏輯對象管理器負責提供邏輯對象的管理接口,比如邏輯對象的創建,刪除等操作。
? 數據庫訪問層:數據庫訪問層主要由數據庫訪問器接口和數據庫訪問器實現來組成。這一層提供具體的數據訪問操作給邏輯處理層使用。數據庫訪問器接口和數據庫訪問器實現分屬于不同的程序集,這樣就使得數據庫訪問器實現能很方便的被替換,使系統能遷移到使用新的數據庫系統,只要我們提供新數據庫系統的數據庫訪問器實現,并且更改數據庫訪問層的程序配置文件指向新的數據庫訪問器實現。
系統具體設計
本章節按照“系統總體架構介紹”里面描述的模塊順序逐個介紹這些模塊的具體實現策略。
請求監聽層:
請求監聽模塊:
設計請求監聽模塊涉及到設計我們所使用的具體的網絡協議的格式,在初步的設計里面,我們使用 .Net對于 WinSocket的封裝來實現網絡數據的傳輸,使用TCP/IP協議。在TCP的payload里面傳輸我們的請求消息和響應消息。消息格式如下:
其中各個字段的含義如下:
Command Value: 用于指示操作碼,如果系統最高位是0,表明這是系統定義的操作碼;如果最高位是1,則表明是用戶定義的操作碼。對于每一個特定的操作碼,系統應該提供一個該操作碼下的Request Data的解析函數;所以,如果用戶需要定義自己的操作碼,那么他也應該提供該操作碼下的Request Data的解析函數實現。解析函數有特定的格式如下:
public Dictionary
RequestDataParser(ushort commandValue, ushort flags, Guid sessionId, byte[] requestData);
其輸入為一個完整的包結構,輸出為解析出來的Request Data中包含的有意義的字段對應C#中的數據類型,以及這些數據的值。這樣子就是得我們能在我們的請求監聽層初始化的時候根據配置文件中的解析函數配置注冊合適的解析函數,在運行中能夠調用注冊好的配置函數解析對應的網絡數據將其轉換為有意義的邏輯數據。
在這種設計之下,用戶如果需要定義自己的Command Value,假設其值為0xF000,那么用戶提供一個叫做CustomeRequestDataParser.dll的程序集,其中包含了對于Command Value0xF0000下的Request Data的解析函數實現,其實現如下:
public class CustomeRequestDataParser
{
public Dictionary RequestDataParser(ushort commandValue, ushort flags, Guid sessionId, byte[] requestData)
{
Dictionary result = new Dictionary();
if (requestData.Length != 8)
{
throw new ArgumentException("Invalid network data received.");
}
byte[] field1 = new byte[4];
byte[] field2 = new byte[4];
Array.Copy(requestData, field1, 4);
Array.Copy(requestData, field2, 4, 4);
result.Add(
typeof(uint),
BitConverter.ToUInt32(field1, 0));
result.Add(typeof(uint), BitConverter.ToUInt32(field2, 0));
return result;
}
}
在我們的請求監聽層的程序配置文件中存在這么一項:
在我們的請求監聽層的初始化代碼中存在這么幾行代碼:
public void RegisterRequestDataParsers()
{
System.Collections.Specialized.NameObjectCollectionBase.KeysCollection keysCOllection = ConfigurationSettings.AppSettings.Keys;
List requestDataParserKeys = new List();
foreach (string keyValue in keysCOllection)
{
if (keyValue.Contains("CommandValueParser"))
{
requestDataParserKeys.Add(keyValue);
}
}
foreach (string keyValue in requestDataParserKeys)
{
string assemblyPath = GetAssemblyPath(ConfigurationSettings.AppSettings[keyValue]);
if (!IsAssemblyLoaded(assemblyPath))
{
AppDomain.CurrentDomain.Load(assemblyPath);
}
string typeName = GetTypeName(ConfigurationSettings.AppSettings[keyValue]);
ushort commandValue=GetCommandValue(ConfigurationSettings.AppSettings[keyValue]);
if (!registeredRequestDataParsers.ContainsKey(commandValue))
{
registeredRequestDataParsers.Add(commandValue, Type.GetType(GetTypeFullName(typeName)));
}
}
}
當請求監聽模塊收到一個請求之后,它會將其轉換為一個事件,將其放進事件隊列中,一個事件應該包含以下信息:
public class Event
{
private DateTime timeStamp;
private ushort commandValue;
private ushort flags;
private Guid sessionId;
Dictionary requestData;
}
一個時間隊列的可能定義如下:
public class EventQueue
{
private List eventPool;
private uint eventCount;
private Event mostPrioritizedEvent;
}
請求調度模塊不斷的查詢事件隊列中的時間,按照特定的調度策略找出一個合適的事件交給邏輯層處理:
public abstract class EventSelectionPolicy
{
}
public class EventQueueProcessor
{
private EventQueue eventQueue;
private EventSelectionPolicy eventSelectionPolicy;
public void RegisterEventQueue(EventQueue queue)
{
if (this.eventQueue != null)
{
throw new ArgumentException("Unable to register another event queue when a queue is still in processing.");
}
this.eventQueue = queue;
}
public EventQueue UnregisterEventQueue()
{
if (this.eventQueue == null)
{
throw new InvalidOperationException("Unable to unregister an event queue when the queue is not intialized.");
}
EventQueue queue = this.eventQueue;
this.eventQueue = null;
return queue;
}
public void RegisterEventSelectionPolicy(EventSelectionPolicy policy)
{
this.eventSelectionPolicy = policy;
}
public Event SelectEvent()
{
//
//return ...;
}
}
EventQueueProcessor調用SelectEvent方法按照注冊過的調度策略選擇一個event交給邏輯處理層處理。
后記