Author:
Anders小明
以前寫過一篇《基于抽象的分層結構》,這里補充一篇《基于業務模塊組件的系統架構》
一些內容在《項目筆記:dao,web,模塊邊界以及Model分類》以及《模塊的接口設計》隨筆中已經提到,這里補充總結一下。
任何一個有一定規模系統,通常會把系統做一定分解降低分析設計開發的難度,模塊劃分是一個比較常見的方式。
而在模塊的劃分及其分析設計的實踐中,包括了兩種層次的邊界。第一是交互行為層次,第二是對象層次。
首先說交互行為。
模塊和模塊的交互接口最為重要,通常我們認為這些接口應該通用穩定,然而如何設計每個模塊對外提供的接口卻是一個不易的問題。
集成的兩種基本實現方式:
1. API方式。
這是常見的實踐方式。即每個模塊公開數量不多的接口類(但擁有較多的接口方法)以及接口參數(都是普通的VO),每個接口的設計和實現都有該模塊成員維護;實踐中:這些接口都獨立打包,形成多(模塊)對一(接口)的依賴關系,方便編譯。
這樣的好處是:接口邊界清晰,開發編譯依賴比較簡單。
然而實踐中極有可能出現兩種狀況:接口維護失控或者過嚴而死板(而影響開發)。接口失控是因為接口的維護太過隨意,因為A模塊的需要就輕易在B模塊中添加一個接口(方法),導致該接口(方法)非獨立性(基本上只給模塊A的這個功能點使用),或者是接口的控制過嚴,導致或者工作效率不高,或者接口的易用性不好。
原因在于:接口是兩個模塊間的耦合,而發生的種種問題在于模塊耦合太過緊密;同時實踐中,把模塊對外提供的接口,與模塊需要實現的外部模塊的集成混為一談;換句話說,A模塊需要集成的語義,和B模塊提供接口的語義存在差距。
這樣的實踐優點:開發管理控制成本較低,模塊編譯依賴簡單,模塊物理邊界明確。
2. Adapter方式
這種的實踐可以一定程度上解決API方式面臨的問題:根據指導原則——為了降低耦合只有在中間加一層;即不輕易為模塊設計對外提供的接口(方法),除非是通過重構得來的;模塊對外提供兩種類:一個是需要外部模塊實現的接口(接口設計從本模塊需要出發,當然每個接口盡管是為某個功能點服務,但也要注意其在模塊內通用性);另一個是其它模塊要求本模塊實現的接口的實現類(這塊物理上獨立打包,獨立于模塊之外)。
即:A模塊擁有一些需要B模塊實現的接口(A模塊對B模塊的要求),而B模塊中也有要求A模塊實現的接口,因而A有這些接口的實現類。
處于編譯依賴的考慮,已有的實踐是把處理接口適配的代碼維護在模塊外的,就是所謂的集成模塊,這樣編譯上就形成了1(接口實現)對n(模塊)的依賴關系。模塊的邊界在這里模糊了,當然在這之外模塊的邊界是很清晰的。
這種實踐方式的好處在于:模塊的接口就多了一層隔離降低了耦合,把接口的通用性和接口的適應性分離,又明確了模塊的邊界,使得接口在日后的優化和調整有了緩沖。
這樣的實踐和API實踐的不同之處在于:接口的設計權利一分為二,需求方擁有了其應該有的權利,并且這個權利與提供方沒有沖突(舊的模式不行,一旦供應方改變了接口,就立刻導致編譯問題)。
兩種方式的對比:
1. 采用API方式集成,代價在于維護API本身,API面對眾多的客戶端,其面臨的設計維護成本較高,同時實施成本高;
2. 采用Adapter方式集成,由于沒有維護API,缺乏可復用接口,但可以做到為每個客戶端自由定制,其設計維護成本較低,實施上成本較高;
集成的演進方式:
1. 采用事件模式。
這也是完成交互處理的模式。通常事件模式在業務行為上也可以完成和接口相互的功能,不過由于其在接口簽名上無法明確提供其業務含義,建議謹慎應用。對于產品化項目,項目開始可以應用事件處理機制,在成熟時提取為更具體的接口。
事件模式中,Event的監聽類也面臨著模塊的邊界問題。已知的成熟開源項目都在事件源中暴露的模塊內部對象,方便開發者使用。
可以說這種方式的是Adapter方式的演進;
2. 采用框架回調方式
工作流和頁面流也負責集成不同的模塊.不過因為工作流和頁面流框架都維護獨立的數據對象共享池,因而在這個層面上,模塊間直接的交互并不存在。這種集成上的優勢,演化成通過ESB等集成框架完成.
這種方式是API方式的演進,但是把調用點以及調用方獨立出來,成為框架的一部分。
現在說說對象一級的邊界集成。
在保險中,benefit對象會關聯一個product對象,不過product對象是是屬于產品模塊而非保單模塊,對于保單模塊只關心id而并不使用對象,但在RMS或者FMS或者UIC這樣的集成環境卻是需要product對象的。我們要采用代碼生成的方式,在benefit對象上加一個annotation,比如Integration的annotation標識,使用AspectJ在編譯上enhancement生成的class,使得benefit對象在集成環境上可以拿到product對象,這算是一個集成方面。
組件化管理
組件化管理包含兩個含義:
1. 組件訪問控制,依賴管理以及服務注冊;
現有的訪問控制的辦法,只能通過代碼提交時或者編譯發布時檢查,但我們更期望支持運行時的能力,避免外部組件訪問組件未申明公開的內部結構和程序;同時維護方式應該簡單方便,成本低。
目前的組件依賴管理幾乎沒有,尤其是基于版本的依賴;而在實踐中,依賴維護管理尤其是多版本的通?;ㄙM大量的成本。
2. 組件依賴的綁定以及失效(故障)的隔離;
我們希望能夠為每個組件提供一個保護區域,當其依賴的一個組件因為種種原因(軟件錯誤的或者軟件升級)失效時,系統至少應該提供一種fail-fast機制,包括如下能力:
A.故障即停(Halt on failure)——當一個組件出錯時,應當立即停止下來,而不是繼續執行可能不正確的操作;
B.故障曝光性質(Failure status property)——當一個組件發生故障時,系統中的其他組件應該得到通知,故障的原因必須交代清楚;
同時,我們希望該組件,如果可能它應該可以繼續運行。完成這樣的目標需要很多的工作,但首先依賴于系統fail-fast能力,同時在故障消除服務恢復后,系統能夠提供相應的通知,并控制恢復的順序。
已知的支撐框架:SCA和OSGi
1. SCA考慮的是異構異步環境,OSGi考慮同構同步環境;
2. OSGi規定了容器如何管理組件,組件依賴(版本)管理,組件邊界保護以及組件服務依賴運行時綁定策略和權限控制;SCA對此沒有涉及;
3. SCA考慮的如何暴露服務,設計時客戶端如何使用服務;OSGi考慮的更加完善;