【正文】
在市場推廣方面,銷售反而是順理成章的事情。 ? 業(yè)務(wù)實體的的增、刪、改及一些復(fù)雜的業(yè)務(wù)處理功能:對于 SAAS RES 系統(tǒng)來說,業(yè)務(wù)實體的增、刪、改是最常見的操作,它們屬于業(yè)務(wù)實體的維護操作,在系統(tǒng)的產(chǎn)品管理、客戶管理、銷售管理、服務(wù)管理和財務(wù)管理模塊都普遍存在,這些操作都包括一些數(shù)據(jù)校驗,業(yè)務(wù)約束等處理邏輯,除了業(yè)務(wù)實體的增、刪、改等數(shù)據(jù)維護操作外,一些復(fù)雜的業(yè)務(wù)處 理功能也具有相當(dāng)?shù)牡湫托?,這些復(fù)雜的業(yè)務(wù) 處理通常涉及多個業(yè)務(wù)實體的狀態(tài)變化以及它們之間的業(yè)務(wù)約束,而且這些業(yè)務(wù)邏輯很大機會隨著業(yè)務(wù)的發(fā)展而變化,因此,如何通過合理的設(shè)計來保證以一個清晰的結(jié)構(gòu)來表達這種業(yè)務(wù)模型,并能夠讓模型有足夠的靈活性來應(yīng)付將來可能發(fā)生的業(yè)務(wù)變化顯得非常的關(guān)鍵,在架構(gòu)分析中,將會以“客戶下訂單(即創(chuàng)建訂單)”作為用例進行分析。 ? 并發(fā)數(shù): 5% * 平均在線人數(shù) = 625 次 每秒 ? 性能:系統(tǒng)及時提供相應(yīng)服務(wù)的能力。如當(dāng)業(yè)務(wù)量增大時 (比如用戶操作頻繁或者增加了租戶) ,可以通過增加服務(wù)器來提高性能,無需對系統(tǒng)進行編程級修改,也不會對最終用戶的使用產(chǎn)生影響。對故障的處理必須簡單快速。 “用戶”和“賬戶”在本文的描述中都是只用戶用來登陸系統(tǒng)的用戶名。 ? 領(lǐng)域?qū)樱?包含業(yè)務(wù)領(lǐng)域內(nèi)的一系列相關(guān)領(lǐng)域?qū)ο蠹捌鋫}庫接口,領(lǐng)域?qū)ο蠓庋b其領(lǐng)域內(nèi)的業(yè)務(wù)邏輯,領(lǐng)域?qū)ο笸ㄟ^其倉庫獲取和持久化。經(jīng)過上述分析,得出下述的時序圖: 修改領(lǐng)域?qū)ο髸r序圖 用例 3:把個人客戶信息放入回收站 —— 業(yè)務(wù)實體的刪除操作 把個人客戶信息放入回收站有如下的業(yè)務(wù)約束: ? 參加過認籌登記的客戶不能放入回收站; ? 交過錢(存在 財務(wù)付款記錄)的客戶不能放入回收站; ? 下過訂單的客戶不能放入回收站 ? 正在提交審核(被修改或合并后未審核通過)的客戶不能放入回收站。 決策: 由于對象的查詢業(yè)務(wù)、報表業(yè)務(wù)沒有對業(yè)務(wù)實體的狀態(tài)產(chǎn)生影響, 因此嚴(yán)格來說,它們并不屬于“業(yè)務(wù)”,我們可以以一種“查詢通道”的方式來實現(xiàn),查詢通道的意思是:不加載領(lǐng)域?qū)ο?,也不使?ORM,而是直接使用結(jié)構(gòu)化查詢語言 SQL(對于不同的持久化實現(xiàn)機制,有不同的結(jié)構(gòu)化查詢語言,如 XML 的 XQuery、 XQL 等),通過組合查詢直接返回數(shù)據(jù)視圖所需要的數(shù)據(jù),并組裝成一個 DTO 返回 ( 注:這只是其中一種實現(xiàn)方案,還有一種比較可行的實現(xiàn)方案是:為需要查詢的數(shù)據(jù)在數(shù)據(jù)庫建立數(shù)據(jù)視圖,然后通過 ORM 工具把視圖映射為一個 DTO,這種方式就不需要直接編寫 SQL 語句,但返回的對象并不是 DO,而 是沒有任何業(yè)務(wù)含義的 DTO) ,下圖展示了查詢通過的邏輯層關(guān)系圖: 展 示 層 ( W e b 層 ) 應(yīng) 用 服 務(wù) 層 數(shù) 據(jù) 訪 問 層( 查 詢 通 道 )S e r v i c eS e r v i c eS e r v i c eA c t i o nA c t i o nA c t i o nQ u e r y S e r v i c eD T OD T OD T O圖表 查詢業(yè)務(wù)、報表業(yè)務(wù)設(shè)計決策圖(查詢通道、貧血模型) 與“充血模型”不同,應(yīng)用服務(wù)層繞過了領(lǐng)域?qū)樱苯油ㄟ^查詢通道查詢數(shù)據(jù),并把查詢結(jié)果直接以 DTO 的形式返回,下面是使用查詢通道查詢數(shù)據(jù)的時序圖: 查詢通道時序圖 這種方式,免去了加載無用數(shù)據(jù)、對象關(guān)系映射、領(lǐng)域?qū)ο笈c DTO 的轉(zhuǎn)換邏輯,從而大大的減輕了磁盤 IO、 CPU 開銷和內(nèi)存開銷。悲觀鎖對數(shù)據(jù)的處理采取一種保守的策略,即在整個數(shù)據(jù)處理過程(完整的事務(wù))中,對目標(biāo)數(shù)據(jù)上鎖,以保證事務(wù)以外的其他操作都無法在此事務(wù)提交前對目標(biāo)數(shù)據(jù)進行修改,悲觀鎖的實現(xiàn)通常依賴于數(shù)據(jù)庫提供的鎖機制。 分析: SaaS RES 對于功能權(quán)限和數(shù)據(jù)權(quán)限都有需求,對于功能權(quán)限控制,系統(tǒng)采用公司開發(fā)的通用權(quán)限組件 實現(xiàn), 同時,由于系統(tǒng)的數(shù)據(jù)權(quán)限是根據(jù)組織架構(gòu)進行劃分的,數(shù)據(jù)權(quán)限控 制的組織架構(gòu)還會采用公司開發(fā)的通用組織架構(gòu)組件實現(xiàn) , 通用權(quán)限組件 以一種事件攔截的機制來實現(xiàn)基于 RBAC 的功能權(quán)限控制,其原理如下圖: 圖表 通用權(quán)限組件 當(dāng)用戶試圖訪問系統(tǒng)的功能服務(wù)時,功能權(quán)限攔截器會首先把用戶的請求攔截下來,然后把用戶信息和請求信息發(fā)送給功能權(quán)限控制器,功能權(quán)限控制器會根據(jù)用戶信息從權(quán)限數(shù)據(jù)庫中獲 取該用戶的授權(quán)信息,即角色集合,然后再獲取這些角色的功能權(quán)限集合,最后判斷出該用戶是否擁有其請求的功能服務(wù)的訪問權(quán),如果有,則請求被傳遞給相應(yīng)功能服務(wù),如果沒有,請求被拒絕,并重定向到拒絕訪問的錯誤頁面。可以看到,兩種方案都需要編寫等量的數(shù)據(jù)權(quán)限控制代碼,但后者可以讓系統(tǒng)的結(jié)構(gòu)更加清晰、合理和容易維護,因此,后者是一種更合適的選擇。 基于 單域名的租戶訪問方式 由于用戶訪問方式是單域名的,比如 沒有登陸之前,我們是沒有辦法知道正在請求的用戶是屬于哪個租戶的。在這里,我們使用公司的單點登陸組件(基于 CAS)。因此,這對我們的設(shè)計提出了更高要求,要盡量的減少網(wǎng)絡(luò)帶寬的占用,減少不必要的網(wǎng)絡(luò)傳輸,提升 web 的性能等等。比如當(dāng)租戶只有 1000 以內(nèi)的時候,一個 MySQL 實例差不多可以滿足了;當(dāng)數(shù)量為 的時候,可能就需要兩個 MySQL 實例;如果再更多的,那么可能會需要 N 個 MySQL實例。 從上面可以看出,這里所謂的“敏感數(shù)據(jù)加密”實際上就是指數(shù)據(jù)內(nèi)容本身的安全加密,這些數(shù)據(jù)是要被加密后存儲在 SaaS 服務(wù)提供商的數(shù)據(jù)庫中的。這也是件比較危險的事,萬一“數(shù)據(jù)密文”泄露了怎么辦?因此同樣需要對這個“數(shù)據(jù)密文”進行加密操作保護起來。 “數(shù)據(jù)密文”對于租戶、用戶來說是透明的,是租戶下所有用戶 共有的,因此沒有修改的必要,但是用戶的密碼是可以修改的。 這里有個集合關(guān)系: RES 系統(tǒng)全部的 產(chǎn)品模塊 集合 租戶擁有 產(chǎn)品模塊 租戶下用戶擁有的產(chǎn)品模塊 。該“賬號引擎”目前可以簡單的實現(xiàn)為一個 web 服務(wù)。也就是采用 JMS 中的消息訂閱模式。 大致流程是: 1. BOSS 系統(tǒng)把調(diào)整后的產(chǎn)品模塊 信息發(fā)布到 ESB 上 2. “ 產(chǎn)品模塊 引擎”獲取這些產(chǎn)品模塊配置 信息 3. “ 產(chǎn)品模塊 引擎”把這些 產(chǎn)品模塊 信息同步到租戶 對應(yīng)的 數(shù)據(jù)庫中去 4. “ 產(chǎn)品模塊 引擎”把處理的結(jié)果反饋給 ESB 上 5. BOSS 系統(tǒng)可以在 ESB 上獲取權(quán)限信息的處理情況 下面的圖中按序號標(biāo)明了系統(tǒng)處理的大致順序 : 內(nèi) 部 運 營 支 撐 系 統(tǒng)E S B業(yè) 務(wù) 系 統(tǒng)R E S B O S S租 戶 A對 應(yīng) s c h e m a產(chǎn) 品 模 塊 引 擎5.獲取信息處理結(jié)果3.同步租戶的權(quán)限信息權(quán) 限 信 息D e s t i n a t i o n1.調(diào)整租戶的權(quán)限2 . 獲 取租 戶 的權(quán) 限 信息3.同步租戶的權(quán)限信息結(jié) 果 信 息D e s t i n a t i o n4 . 反饋 信 息處 理 結(jié)果租 戶 B對 應(yīng) s c h e m a圖表 產(chǎn)品模塊分配等與租戶相關(guān)信息交互圖 上圖只是對“ 產(chǎn)品模塊 權(quán)限”這樣一種信息交互的一個示例。 BOSS 運營系統(tǒng)與 RES 業(yè)務(wù)系統(tǒng)的消息交互 由上面的分析, 針對 BOSS 運營系統(tǒng)與 RES 業(yè)務(wù)系統(tǒng)的消息交互,可由 下圖 來表示 : 內(nèi) 部 支 撐 系 統(tǒng)E n t e r p r i s e S e r v i c e B u s ( 主 要 適 于 系 統(tǒng) 間 整 合 )業(yè) 務(wù) 系 統(tǒng)R E S B O S SR E S集 中認 證中 心 圖表 BOSS運營系統(tǒng)與 RES業(yè)務(wù)系統(tǒng)總體 關(guān)系 圖 我們把 BOSS 運營系統(tǒng)與 RES 業(yè)務(wù)系統(tǒng)之間交互的消息分為三種類型: 租戶、用戶的賬戶信息交互 此類信息,首先表現(xiàn)為在 BOSS 系統(tǒng)中創(chuàng)建租戶賬戶,租戶開始使用系統(tǒng)之后,在業(yè)務(wù)系統(tǒng)中可以再創(chuàng)建用戶的賬戶。這個過程主要在BOSS 上完成。對于這種情形,我們整理下 流程:首先,“租戶管理員”在新建租戶的時候就由系統(tǒng)生成了該租戶的“數(shù)據(jù)密文”, 假設(shè) 我們新建了租戶 A 的“ 租戶第一人 ”,那么“數(shù)據(jù)密文”是同這個“ 租戶第一人 ”的數(shù)據(jù)一起存儲的(經(jīng)過了“ 租戶第一人 ”密碼的對稱加密)?!胺菍ΨQ加密”會比“對稱加密”的計算時間要長,有近 2~3 個數(shù)量級的差別,因此,出于性能上的考慮,我們使用“對稱加密”這種方式。 另外, 在《 SaaS 模式系統(tǒng)參考架構(gòu)設(shè)計 .doc》中論述了“完全動態(tài)路由”的概念,感興趣的可以去該文檔中查閱。因此,這個限制使得 一個 MySQL 數(shù)據(jù)庫實例只可為近 1000 的租戶提供服務(wù),如果租戶超過 1000 那就需要另外準(zhǔn)備一個 MySQL 實例了。舉個例子來說:租戶 A 新增了 usera,userb 這兩個賬戶,那么租戶 B 就不可以新增同名賬戶了。由于我們這一期,不會對租戶提供界面?zhèn)€性化的功能,因此 ,用戶登陸后的界面風(fēng)格是一致的。 支持多租戶的數(shù)據(jù)結(jié)構(gòu) 由于目前已經(jīng)確定使用 MySQL 作為數(shù)據(jù)庫的約束,要支持多租戶,我們有以下三種方式可以選擇: 1. 每個租戶都有獨立的數(shù)據(jù)庫 2. 租戶共享同一個數(shù)據(jù)庫,但獨立數(shù)據(jù)結(jié)構(gòu)(即一個租戶對應(yīng)一個 schema) 3. 租戶共享同一個數(shù)據(jù)庫,且共享相同的數(shù)據(jù)結(jié)構(gòu)。對于數(shù)據(jù)權(quán)限控制,權(quán)限組件 只提供了擴展的機制,并沒有提供具體的實現(xiàn)。創(chuàng)建訂單是一種高并發(fā)的操作,每個房間在整個下訂過程只被一個客戶占有,這顯然是客戶不能接受的,而事務(wù)孰先孰后,對于客戶來講卻是透明的,很明顯,我們應(yīng)該采用 Hibernate 的樂觀鎖策略來避免房間爭訂的情況。 創(chuàng)建訂單 —— 典型的高并發(fā)資源爭用業(yè)務(wù) 分析: 創(chuàng)建訂單是整個系統(tǒng)一個非常重要的業(yè)務(wù)功能,它在一些特定的時間,如五一、十一 等開發(fā)商促銷的時間并發(fā)性比較高,由于同一個房間 在同一時間 只能下一張訂單,這里就涉及一個在下訂高峰期房間爭訂的情況,系統(tǒng)要保證不要出現(xiàn)同一房間被不同兩個客戶下訂的情況。 用例 4:客戶下訂單(即創(chuàng)建訂單) —— 典型的復(fù)雜業(yè)務(wù)處理功能 客戶下訂單有如下的業(yè)務(wù)約束(注:由于該用例業(yè)務(wù)功能非常復(fù)雜,而且在結(jié)構(gòu)設(shè)計階段,銷售模塊的需求分析還沒完全完成,因此該用例的業(yè)務(wù)約束將忽略一些不是很重要的細節(jié),該用例的最終模型和時序圖以銷售管理模塊的詳細設(shè)計為準(zhǔn)): ? 下訂單 時可以根據(jù)系統(tǒng)參數(shù)的設(shè)置同時修改客戶信息; ? 只有狀態(tài)為“可售”的且有定價的房間可以進行下訂單; ? 當(dāng)系統(tǒng)參數(shù)設(shè)置組合房間可以拆開銷售時,組合房間和基礎(chǔ)房間都可以下訂單,否則,被組合的基礎(chǔ)房間不可售; ? 根據(jù)系統(tǒng)參數(shù)設(shè)置判斷是否可以使用歷史價格; ? 如果有客戶有進行認籌登記,需要對認籌登記進行解籌,并把認籌金額轉(zhuǎn)換為定金; ? 需要根據(jù)各種優(yōu)惠及款項信息計算訂單的總折扣率和折后總房價; 分析業(yè)務(wù)約束,第一個業(yè)務(wù)約束很明顯不屬于訂單實體及其他相關(guān)實體的職責(zé),由銷售管理應(yīng)用服務(wù)委托系統(tǒng)參數(shù)設(shè)置應(yīng)用服務(wù)進行判斷,當(dāng)可對客戶信 息進行修改時,還需要委托客戶管理應(yīng)用服務(wù)提供修改服務(wù);房間是否可售需要根據(jù)房間狀態(tài)決定,這職責(zé)明顯是由房間實體負責(zé);已組合的基礎(chǔ)房間是否可售,由系統(tǒng)參數(shù)設(shè)置決定,所以應(yīng)該由銷售管理應(yīng)用服務(wù)委托系統(tǒng)參數(shù)設(shè)置應(yīng)用服務(wù)進行判斷,至于房間是否已經(jīng)被組合,這是房間實體本身的職責(zé);可否使用房間歷史價格,由銷售管理應(yīng)用服務(wù)委托系統(tǒng)參數(shù)設(shè)置應(yīng)用服務(wù)進行判斷;對于解籌操作,訂單實體包含認籌登記記錄,因此應(yīng)該由訂單負責(zé);訂單的總折扣率和折后總房價是由訂單的各種優(yōu)惠及款項信息決定的,因此屬于訂單的職責(zé),但進一步分析,總折扣率和折 后總房價可能由不同的計算方式,因此,應(yīng)該設(shè)計一個訂單總折扣率和折后總房價計算器實體,并由訂單委托其進行計算,綜上所述,得出如下時序圖: 客戶下訂單時序圖 這個時序圖的設(shè)計并不是最終的客戶下訂單用例的時序圖,它主要表達了一個復(fù)雜的業(yè)務(wù),需要通過合理的抽象以及對業(yè)務(wù)變化的預(yù)見性,建立好合理的模型,并把職責(zé)合理的劃分到不同的服務(wù)或領(lǐng)域?qū)ο笾?,從而得到更好的靈活性或擴展性,這也是領(lǐng)域驅(qū)動設(shè)計的核心思想。 領(lǐng)域驅(qū)動設(shè)計,或者說面向?qū)ο笤O(shè)計的最重要規(guī)則是:把不同的職責(zé)合理的劃分到不同的對象中,根據(jù)這一規(guī)則,我們對上述的業(yè)務(wù)約束進行分析: 客戶屬性是否必填,屬于客戶實體自身的業(yè)務(wù)完整性約束,因此,這部分職責(zé)應(yīng)該交給客戶實體本身負責(zé); 客戶的姓名和聯(lián)系電話是客戶身份的唯一標(biāo)識,這意味著系統(tǒng)不能存在兩個具有相同姓名和聯(lián)系電話的客戶,這 就需要在創(chuàng)建客戶的時候進行檢查,而這部分工作,對于客戶實體來說,它并不了解其他客戶實體的信息,因此,應(yīng)該交由其上層,客戶管理應(yīng)用服務(wù)負責(zé)。 架構(gòu)分析 下面主要綜合之前的功能性和非功能性的要求,來對系統(tǒng)的整個架構(gòu)進行分析。 指 SaaS 服務(wù)提供商內(nèi)部,負責(zé)管理“租戶”的管理員,該管理員的職責(zé)一般是根據(jù)合同來添加租戶,添加租戶的“ 租戶第一人 ”, 修改租