【51CTO.com快譯】不知道您是否聽說過“軟件架構師最討厭意大利面”這個梗?它是指軟件架構師在設計應用系統(tǒng)時,應當在匹配業(yè)務概念的基礎上,開發(fā)出清晰的架構與流程,避免出現(xiàn)各種在邏輯上相互纏繞,模塊與層面關系定義不清,各個功能彼此交織,進而形成難以被運維的“意面式架構(spaghetti architecture)”。下面,我將總結一些值得您遵循的應用架構優(yōu)秀實踐,以便您構建出結構化的、可擴展的應用架構。
為什么應用架構如此重要?
通常,應用架構包含了所有的軟件模塊、組件、內(nèi)/外部系統(tǒng)、以及構成應用之間的交互關系。顯然,結構良好的應用架構,可以確保您的應用能夠根據(jù)業(yè)務和用戶的需求進行預期的擴展。同時,好的架構既能夠合理地隔離不同的功能概念,又可以在內(nèi)/外部形成良好的依賴關系。
相反,如下圖所示,如果您在針對各種需求的初期設計時,以及后期的變更中,忽略了對于應用架構的合理構建與維護,那么將會導致不同組件之間,依賴關系的錯綜復雜,甚至難以同步與管理。
那么在實際項目中,意面式的架構到底會給我們的系統(tǒng)帶來哪些危害呢?
- 服務抽象性差:如果未能圍繞著核心業(yè)務概念,實現(xiàn)正確地隔離和抽象,那么服務就會在不同系統(tǒng)之間分散各種業(yè)務規(guī)則,進而降低代碼的結構化和可重用的程度。
- 難以管理的依賴關系:當組件之間無法被恰當?shù)馗綦x時,任何針對系統(tǒng)的修改或替換操作,都會產(chǎn)生滾雪球式的效應。也就是說,某一部分的更改,會影響到與之相關聯(lián)的所有依賴關系。
- 僵化且運行緩慢的舊系統(tǒng):如果系統(tǒng)本身就很復雜且不靈活,那么我們就需要花費更長的時間,通過調(diào)整來適應新的業(yè)務變化。而且,如果所用到的技術已經(jīng)過時,那么隨著時間的推移,核心數(shù)據(jù)與系統(tǒng)會對過時的技術,累積深度的依賴性,從而導致技術更新變得難上加難。
如何構建可擴展的應用架構?
為了構建可靠且可擴展的應用架構,您需要基于嚴格的定義原則和完善的設計概念。顯然,我們的目標是:既需要支持快速的業(yè)務增長和大規(guī)模的擴容需求,又需要降低部署的難度并避免高昂的代碼維護成本。因此,我們可以從如下方面考慮應用架構的設計:
- 在所有項目參與者之間達成共識。
- 支持定義和計劃。
- 持續(xù)進行變更。
- 管理好系統(tǒng)的復雜性。
- 管控與降低風險。
- 最大限度地減少技術債務(這是任何前瞻性應用架構的最終目標)。
在此,我們引入一個架構畫布(Architecture Canvas)概念。作為一個支持和加速架構設計的多層框架,它可以促進對可重用的服務和組件進行抽象。通過保留相對獨立的生命周期,架構畫布可以最大程度地減少變更所帶來的影響,進而使得應用架構更易于維護和擴展。
架構畫布的邏輯組成如上圖所示。其中,從下往上分別是:
- 基礎層:在該層中,您可以實現(xiàn)所有可重用的非功能性需求。例如:連接到外部系統(tǒng),或者使用可重用的UI模式與主題庫,來擴展現(xiàn)有的框架服務。
- 核心層:在該層中,您可以實現(xiàn)各種核心的業(yè)務服務。例如:各種圍繞著業(yè)務概念的服務、業(yè)務規(guī)則、業(yè)務實體、業(yè)務交易和業(yè)務部件等。您需要讓這些服務獨立于目標系統(tǒng),并根據(jù)基礎服務來抽象出任何可能的整合信息??梢姡ㄟ^基礎層和核心層,您已經(jīng)隔離出了所有可重用的服務或組件。
- 最終用戶層:在該層中,您可以通過使用基礎與核心層的服務,來支持用戶界面,以及與用戶交互的流程。值得注意的是:為了確保整個生命周期的獨立性,處于該層面上的模塊,不應為其他模塊提供服務。
架構的驗證
為確保設計架構的合理性,且不會產(chǎn)生“意面式”的爛尾,下面我將為您提供一些可以遵循的準則和建議。
1.不要帶有橫跨三個層面的向上引用
鑒于前文提到的結構化分層,我們顯然不應該讓與業(yè)務無關的基礎服務,去依賴核心業(yè)務;也不應該讓可重用的服務,依賴各種最終用戶的接口。此外,向上引用往往會產(chǎn)生一個群集。如下圖所示,在該群集中,存在直接或間接鏈接關系的任何兩個模塊,都具有循環(huán)依賴性。
在上圖中,由于模塊B可以間接地影響模塊A,而模塊A也可以間接地影響模塊B,因此,這就是一組相互依賴的模塊。此外,如果您有另一個正在使用核心服務B的最終用戶模塊(EU2),那么它就會依賴整個群集。可見,它們在運行時,不僅會占用大量不必要的資源,還會受到集群中某些模塊變化的間接影響。
2.避免最終用戶之間的旁路引用
為了確保正確的隔離,并避免最終用戶具有不同的生命周期,最終用戶模塊不應提供可重用的服務。下圖展示了最終用戶之間的旁路引用關系。
也就是說,如果最終EU1調(diào)用到了EU2,則表明EU1無法獨立于EU2,同時他也就不能獨立于EU2下面的層級結構中的集群。
3.避免在核心模塊和基礎模塊之間進行循環(huán)引用
如果您能夠遵循前面提到的兩個規(guī)則,那么就不必擔心最終用戶模塊之間可能出現(xiàn)循環(huán)引用。反而,我們應當重點避免在核心模塊和基礎模塊之間,可能出現(xiàn)的循環(huán)引用。此類模塊之間的循環(huán)引用主要產(chǎn)生于:一些業(yè)務概念沒能被正確地抽象,進而對代碼的管理產(chǎn)生不良的影響。
如上圖所示,循環(huán)引用多發(fā)生在如下兩種情況中:
- A和B之間的連接相當緊密,甚至它們隸屬于同一模塊(例如,某個訂單或訂單項)。
- 根據(jù)兩個概念之間的既定關系,如果改變一個模塊的邏輯位置,其單方面的依賴關系就會被破壞。例如,合同是由客戶產(chǎn)生的,但是客戶的存在則無需合同的引用。
4.額外的建議
- 核心模塊不應具有前端的篩選條件:如果要實現(xiàn)某個服務,您可能需要添加一些篩選條件,用以進行單元測試。但是作為開發(fā)人員,一旦完成了代碼測試,就應該及時去除掉測試的篩選條件。如果出于某種原因,仍需要使用測試篩選條件,來支持某些回歸測試或BDD(行為驅(qū)動開發(fā))測試的話,您就需要將其移至最終用戶的測試模塊中。畢竟,將測試篩選條件保留在核心模塊上,是非常危險的?;陲L險管控的考慮,它們只能存在測試環(huán)境中,而不能留在生產(chǎn)環(huán)境里。
- 所有實體都應當被發(fā)布為只讀:通過該實踐,您可以禁止訪問者(consumers)簡單粗暴地在數(shù)據(jù)庫中創(chuàng)建、更新或刪除記錄。在核心服務層面上,您應當抽象出業(yè)務事務、驗證、規(guī)范化、以及審核等需要與其他系統(tǒng)集成的組件。在實際項目中,正確的做法是:將所有的業(yè)務交易的實施,都發(fā)布給使用者,同時提供安全且恰當?shù)某橄蠓铡?/li>
- 避免在基礎層面上使用業(yè)務邏輯:有時候,人們會傾向于在該層面上實現(xiàn)各種業(yè)務規(guī)則。但實際上,我們應當確保它與業(yè)務無關,并能在任何應用領域中被重用。
- 不要在基礎層面上添加核心業(yè)務實體:為了與業(yè)務無關,基礎模塊不應具有與業(yè)務相關的實體。不過,它們可以通過帶有非業(yè)務的實體,以支持應用的某些非功能性需求。例如:如果您需要創(chuàng)建通用的服務,來審計所有事務,那么就可以創(chuàng)建一個審計實體。畢竟,某個軟件應用的主要業(yè)務可能并非審計,而是銷售產(chǎn)品,拉新客戶或變更合同等。
使用架構畫布的應用組合
在討論應用組合之前,我先聲明一下:這里所說的“應用”,與我們通常在業(yè)務環(huán)境中所提及的“應用”,具有不同的含義。在該語境中,我們使用術語“應用”來指代 在開發(fā)環(huán)境中的最小部署單元。它既可以是被用于管理的所有環(huán)境,也可以是業(yè)務應用、IT用戶、安全性集合、以及應用單個模塊等。
為了識別應用到底屬于上面提到的哪個層級,您應該對目標應用進行深入分析和模塊化的解構。例如:如果某個應用將帶有最終用戶模塊,那么它肯定屬于最終用戶層面。
下面是一組能夠確保設計出前瞻架構的參考規(guī)則:
規(guī)則1:從模塊的架構畫布準則開始
我們應按照上面給出的建議,對模塊進行正確地分層。
規(guī)則2:隔離公共服務
將各個模塊正確地放置到位后,我們就可以開始設計應用了。如前所述,如果在“最終用戶應用2”上有一個模塊會使用到“最終用戶應用1”上某一個模塊,那么我們就應該對通用核心應用進行隔離,以免產(chǎn)生依賴性。如下圖所示,如果兩個應用要進行內(nèi)容上的共享與交互,則需要在彼此隔離的情況下,通過通用的應用服務來實現(xiàn)。
規(guī)則3:請勿混淆所有者角色
如果一個應用擁有多個所有者,那么由于責任不清,可能會導致變更的內(nèi)容與管理過于復雜。我們可以通過所有權的聚合與分拆(如下圖)兩個方式,給每個應用明確設定一個所有者。
規(guī)則4:分清參與者角色
與所有者角色類似,參與者也有各自不同的節(jié)奏。例如:有一個提供不同保險業(yè)務的門戶網(wǎng)站。其所有業(yè)務線都處于同一個應用中,那么任何一個業(yè)務(例如車險業(yè)務)的任何更改都不可能獨立于其他業(yè)務。因此,實際上是由那個最慢的業(yè)務線,決定了整體應用的發(fā)布周期。
如下圖所示,我們需要通過在每條業(yè)務線中創(chuàng)建單獨的應用,讓每個參與者都可以預估自己的交付速度。在此基礎上,我們可以根據(jù)項目的具體需求,或是將不同參與者的任務相互隔離,或是通過內(nèi)容共享的方式,加強他們的協(xié)作。
原文標題:Application Architecture: Best Practices for Future-Proofing Your Apps,作者: Francisco Menezes
【51CTO譯稿,合作站點轉載請注明原文譯者和出處為51CTO.com】