|
談到應用程序的層次,我們平時所說的層次有兩種:邏輯的層次(layer)和部署的層次(tier)。這兩種層次劃分的目的是不同的,因此劃分方式也有一些差異,能夠為應用程序帶來的好處也是不同的。
邏輯層次
邏輯層次(layer)劃分的最重要的目的在于調(diào)整應用程序各部分之間的依賴關(guān)系。應用程序可以看作數(shù)據(jù)和業(yè)務規(guī)則的集合,這個集合通過用戶界面與用戶發(fā)生交互。如果不劃分層次,或者只劃分最簡單的層次,系統(tǒng)的結(jié)構(gòu)就會是這樣:數(shù)據(jù)庫處于系統(tǒng)的中心地位,在此之上建立用戶界面,業(yè)務規(guī)則寫在用戶界面里。
這樣做的問題在于:數(shù)據(jù)庫作為應用程序的中心是不合適的,因為數(shù)據(jù)庫只負責存儲數(shù)據(jù),而不能對數(shù)據(jù)做出解釋(這是業(yè)務規(guī)則的任務),而業(yè)務規(guī)則分散在非中心的位置,零散的表達在用戶界面中。一旦需求改變,業(yè)務規(guī)則必須隨之改變,而業(yè)務規(guī)則是分散在各處的,我們就要四處尋找業(yè)務規(guī)則,進行修改。隨著應用程序規(guī)模的擴大,這是一項非常艱難的任務。
為了解決復雜的依賴關(guān)系,我們創(chuàng)建了業(yè)務層。典型的邏輯分層結(jié)構(gòu)就是:表示層->業(yè)務層->數(shù)據(jù)層。
建立業(yè)務層的方式可以非常簡單:假如我們的應用程序要進行多項業(yè)務,我們分析這些業(yè)務的流程,找出這些流程中共同的部分,提取他們作為獨立的過程,這就形成了最簡單的業(yè)務層。這樣,不同的界面之間就可以重用業(yè)務規(guī)則的代碼,從一定程度上解決了依賴關(guān)系的不合理性。這是一種基于過程的方法。但是這樣的方法有兩個問題:
第一:他建立在對系統(tǒng)中的各種業(yè)務充分了解的基礎(chǔ)之上,要正確的分析各個業(yè)務流程的細節(jié),否則劃分的流程和提取的共同過程必然有疏漏。但是在實際的開發(fā)過程中,我們往往一開始只了解業(yè)務的整體情況,對于其細節(jié)是逐漸了解的。并且對于一些隱含的關(guān)系,需要在開發(fā)的過程中才能認識到。有時候采用的一些具體的開發(fā)技術(shù)也會影響到某些業(yè)務的實現(xiàn)方式,進而對其流程帶來影響。這樣就為過程的分析帶來很大的困難。
第二:基于過程的方法受到需求變化的沖擊比較大。因為這樣的分析方法,在實現(xiàn)的時候是基于業(yè)務的過程,而不是業(yè)務的目的。當系統(tǒng)中有業(yè)務需要變化的時候,會影響到其他具有共同過程的業(yè)務,業(yè)務之間的依賴是比較嚴重的,對需求變更的適應力差。
如果采用面向?qū)ο蟮姆绞?,將業(yè)務進行抽象,概括實現(xiàn)這些業(yè)務需要使用的手段(例如需要更新數(shù)據(jù)表、向端口發(fā)送指令、保持業(yè)務進行的狀態(tài)……),為所有的業(yè)務制定一個統(tǒng)一的框架,由這個框架決定業(yè)務執(zhí)行的策略(如何限定業(yè)務的條件、分哪些步驟、失敗后是重試還是放棄、何時向用戶報告業(yè)務執(zhí)行的進度……),框架不加區(qū)別的處理所有的業(yè)務。用戶執(zhí)行一個業(yè)務的時候,我們只要相應的創(chuàng)建一個業(yè)務對象,將這個對象置于框架內(nèi),監(jiān)視執(zhí)行的進度。這樣,各個業(yè)務的實現(xiàn)就自然的隔離起來,依賴關(guān)系比較簡單。同時應付需求變更也比較容易,獨立的業(yè)務發(fā)生變更,只要修改一個類,如果多個業(yè)務的執(zhí)行方式都要改變,則可以從框架上想辦法。
如果為這個框架添加新的機制,使他不僅能夠執(zhí)行業(yè)務對象,還能夠負責創(chuàng)建和銷毀業(yè)務對象,就能夠帶來更強大的功能。業(yè)務框架就進而成為一個業(yè)務容器。當用戶希望執(zhí)行一個業(yè)務的時候,我們可以要求業(yè)務容器創(chuàng)建這個業(yè)務對象,業(yè)務容器可以交給我們一個業(yè)務對象的代理,隱藏真實的對象的位置。實際的執(zhí)行過程可能是分布式的,容器為我們管理業(yè)務對象的事務性,保存業(yè)務執(zhí)行的進度,必要的時候暫停業(yè)務,條件成熟后再重新開始,業(yè)務完成以后將對象銷毀……這些我們都不必關(guān)心,由容器為我們做。
優(yōu)化應用程序內(nèi)部的依賴關(guān)系是劃分邏輯層次的一個重要目的,并不是采用了邏輯層次,依賴關(guān)系自然就優(yōu)化了。層次設(shè)計的好才有這個好處,否則就沒有,甚至可能會更麻煩。層次間的調(diào)用機制可以采用逐層調(diào)用,也可以跨層調(diào)用,都是比較常見的方式。層次間的依賴的方向可以從上到下,也可以從下到上,都是合理的。需要小心的是,兩個層次之間不可以產(chǎn)生互相的依賴,否則會破壞層次結(jié)構(gòu)。比如有A和B兩個層次,要么A依賴B,要么B依賴A,不可以互相依賴。如果必須有層次間互相調(diào)用,則必須采用某些方法轉(zhuǎn)換其中一個方向上的依賴關(guān)系(可以采用的方式諸如delegete,interface,消息,事件等等)。多個層次之間也不應該出現(xiàn)循環(huán)的依賴關(guān)系。
部署層次
部署層次(tier)劃分的目的在于增強應用程序部署的靈活性,更大限度的利用硬件環(huán)境資源。應用軟件對環(huán)境有多方面的需要,比如數(shù)據(jù)存儲的模塊需要服務器有較強的穩(wěn)定性、容錯性、較高的IO效率,事務處理模塊則需要運算迅速的處理器和較大的內(nèi)存,而管理終端則需要有較強的表現(xiàn)能力。通常很難有這樣的服務器滿足多方面的需要,因此我們需要將應用程序分為多個層次,部署在不同的位置。
同時,在某些應用程序中需要訪問一些敏感的數(shù)據(jù),這些數(shù)據(jù)是不允許或者不可能集中到本地的,這都要求程序在部署方面增加靈活性。
從系統(tǒng)性能的角度說,應用程序在部署時劃分合理的層次,可以增加系統(tǒng)的擴展性。當現(xiàn)有的硬件條件不滿足需要的時候,可以通過改變部署方式,提高系統(tǒng)的性能。
比如使用一個服務器的時候可以滿足100個并發(fā)業(yè)務,當并發(fā)業(yè)務達到150的時候,可以通過增加服務器滿足這個要求。這就需要應用程序使用多層次的結(jié)構(gòu),缺少必要的層次是不可能進行這樣的擴展的。
再如,我們需要在一個支持20個連接的數(shù)據(jù)庫上建立一個支持100個客戶端的系統(tǒng),這時就必須建立獨立于數(shù)據(jù)庫的連接保持層次。
應用程序的部署層次有一些常用的模式,比如B/S結(jié)構(gòu),C/S結(jié)構(gòu),客戶端/應用服務器/數(shù)據(jù)服務器結(jié)構(gòu),都是一些常用的層次劃分模式。具體采用什么樣的模式要看應用程序的需要,假如應用程序需要頻繁的訪問客戶本地資源,要求較高的效率,或者要求具有強大的離線處理功能,則采用B/S結(jié)構(gòu)就是不合適的,C/S結(jié)構(gòu)是一個比較好的選擇。如果需要在部署配置方面比較簡單,又要面對不同的客戶端環(huán)境,那么B/S結(jié)構(gòu)就是很好的模式。如果需要建立服務器組來平衡負載,那么應用服務器則是必不可少的。
部署層次和邏輯層次有一定的關(guān)系,比如說一個程序從邏輯上不分層次,那么在部署的時候要分為多個層次就很難了。但是這兩種層次并不是必須嚴格一致的,比如說,采用“客戶端/應用服務器/數(shù)據(jù)服務器”的部署層次,并不一定意味著客戶端對應著表示層,應用服務器端對應著業(yè)務層,數(shù)據(jù)服務器就一定是數(shù)據(jù)層。不能把對應關(guān)系絕對化,這兩個角度劃分的層次沒有嚴格的對應關(guān)系,他們是為了達到不同的目的而劃分的。
例如:在一些軟件系統(tǒng)中,將業(yè)務邏輯以存儲過程的形式直接寫在數(shù)據(jù)庫中。這樣的系統(tǒng)中,業(yè)務層與數(shù)據(jù)服務器是相對應的。這樣的業(yè)務層如果處理的好,一樣具有清晰簡潔的邏輯,維護起來也十分方便。但是這樣顯然會在部署上失去靈活性。
現(xiàn)實的問題
很多人在軟件設(shè)計中常有這樣的體會:在設(shè)計一個軟件體系架構(gòu)的時候,為了能夠簡化軟件系統(tǒng)的邏輯復雜性,經(jīng)常采用的手段是將一個大的系統(tǒng)分為若干層次。常用的方法是:分解一個龐大系統(tǒng)的各個功能,將其分別部署在多個部件中,每個部件只完成單一的功能,以減弱部件的復雜程度。部件之間采用一些弱耦合方式進行通信,比如消息、TCP連接等方式。
但是實際的開發(fā)和維護過程中,卻發(fā)現(xiàn)這樣并沒有減輕系統(tǒng)的復雜程度。部件盡管已經(jīng)從物理上分離,運行在不同的線程、進程、服務器上,但是部件之間在邏輯上卻有著強烈的耦合關(guān)系,部件之間的接口有著時間、空間上的復雜交錯關(guān)系,調(diào)用者需要依照復雜的邏輯次序,調(diào)用被調(diào)用者的接口,保存各次調(diào)用之間的狀態(tài),深入到對方的業(yè)務過程細節(jié)中。系統(tǒng)的結(jié)構(gòu)沒有因為層次而變得簡潔,反而更加的復雜。一個業(yè)務的變更會在多個層次中造成影響,變更的代價更加巨大,沒有起到層次模型應有的作用。
造成這個后果的原因在于,設(shè)計者混淆了邏輯層次和部署層次之間的區(qū)別。如果以減小系統(tǒng)的復雜程度為目的,首要的一點是從邏輯上分開層次,讓各層次對業(yè)務的處理處于不同的抽象級別。為業(yè)務行為建立合理的抽象層次,而不是簡單的讓各個部件執(zhí)行業(yè)務的某個階段,也不是簡單的讓部件甲執(zhí)行ABC業(yè)務,部件乙執(zhí)行XYZ業(yè)務,這是邏輯層次的關(guān)鍵。做到了這一點,才能減小系統(tǒng)的復雜程度。錯誤的邏輯層次劃分,只能使系統(tǒng)變得更加復雜。而盲目的劃分部署層次,則是與初始的目的毫無關(guān)系的,也只能增加復雜性。
部署層次劃分的作用并不在于簡化系統(tǒng)邏輯結(jié)構(gòu),而是增加系統(tǒng)對環(huán)境的適應力。
邏輯層次的設(shè)計和部署層次的設(shè)計在軟件開發(fā)中經(jīng)常是交織在一起進行的,他們互相制約,互相影響,有共同的特征。但是這兩種層次劃分方式有著本質(zhì)上的不同,存在著對立的方面,需要開發(fā)者在設(shè)計的時候進行協(xié)調(diào),這也是需要注意的。
|
# re: 應用軟件的層次劃分 2005-09-14 21:02 蛙蛙池塘
文章寫的非常之好,以前我還真分不子清你說的這兩個層的概念,今天徹底的懂了,呵呵,不過想你提到的那種業(yè)務層框架,.NET還沒有吧,spring.net還不夠強大吧。