Cyberwing
FrankSoo是我的項目經(jīng)理。前段時間公司決定作個新的J2EE二次開發(fā)平臺,以替換公司原有的開發(fā)平臺。公司讓FrankSoo和我組成平臺開發(fā)項目組,F(xiàn)rankSoo擔(dān)任項目經(jīng)理。現(xiàn)在這個平臺整合開發(fā)階段已經(jīng)結(jié)束,進(jìn)入項目應(yīng)用階段。下面是我們的整合工作小結(jié),介紹一下我們在工作中遇到的問題,以及我們選擇的解決方案.
1、架構(gòu)的選擇
首先,我們都同意以我們現(xiàn)有的能力,沒有足夠的時間和資源自行開發(fā)一套完整的平臺。在已有的眾多開源項目中選擇若干優(yōu)秀的項目進(jìn)行整合,才可能按時完成項目,達(dá)到項目目的。
但是在平臺項目開始前,我們對平臺的技術(shù)架構(gòu)有各自的構(gòu)想。FrankSoo原來的構(gòu)想是Struts+Spring+Hibernate,而我的構(gòu)想是Tapestry+Hibernate。
不過FrankSoo非常open,在我向他演示了Tapestry的經(jīng)典范例workbench,介紹了Tapestry基于組件的編程方式之后,他同意選用Tapestry作為實現(xiàn)Web展現(xiàn)層的框架。我想FrankSoo以前的Struts開發(fā)經(jīng)驗(painful)也是他做出這個決定的因素之一。
FrankSoo gave me a nice introduction of Spring Framework. Wow, what an amazing framework! IOC, Declarative Transaction Support, Hibernate Session Management, Hibernate DAO Support… These features are just what we need for a middle tire container.
至于Hibernate,這個最成功的開源ORM項目,我們都投了它一票^_^
最后我們確定平臺的技術(shù)架構(gòu)是Tapestry+Spring+Hibernate.
2、架構(gòu)整合
最初的平臺架構(gòu)借鑒了一篇介紹如何集成Tapestry與Spring的文章[1]中提到的架構(gòu):
圖1
Web層的Tapestry負(fù)責(zé)數(shù)據(jù)輸入輸出, 響應(yīng)用戶事件,及輸入校驗的工作, 通過訪問預(yù)先加載的WebApplicationContext(由Spring提供, 包含著所有Service bean)獲得Service層的Service Bean, 把業(yè)務(wù)操作都委托給它們.
Service層的bean則負(fù)責(zé)use case邏輯, domain相關(guān)的邏輯委托給domain model中的bean去實現(xiàn). Service通過DAO完成對domain model的持久化工作. Service負(fù)責(zé)數(shù)據(jù)庫事務(wù)和Hibernate Session的管理(通過Spring的聲明式事務(wù)管理和與之集成的Hibernate Session管理). Service層的另一項重要工作是權(quán)限和訪問控制。
Domain model負(fù)責(zé)表示問題域的數(shù)據(jù)和domain logic. DAO使用Hibernate持久化數(shù)據(jù)以及查詢. 在實現(xiàn)DAO時, 我們使用了Spring的Hibernate DAO Support,極大地簡化了代碼, 很多方法都只用簡單的一行完成. 有意思的是, 最后完成的HibernateDAO的代碼量居然比我寫的MockDAO的代碼少了一半還多
這樣的架構(gòu)優(yōu)點很明顯, 層次清晰, 各層的職責(zé)也明確, 便于分層設(shè)計與開發(fā), 結(jié)合mock和spring的IOC, unit test也是非常容易的. 而且后臺(Service, domain model and DAO)的代碼不依賴于Web容器或是EJB容器的API, 移植性非常好, 同樣的代碼可以在Web app中使用也可在普通的Java app中使用, 只需更換UI層.
按照這個整合的構(gòu)架,我們實現(xiàn)一個簡單的實例,實現(xiàn)了列表分頁查詢和顯示,數(shù)據(jù)增刪改,基于Hibernate Criteria Query提供了一個比較通用的查詢機(jī)制。利用Middlegen和Velocity我們可以從已經(jīng)建好的數(shù)據(jù)庫表結(jié)構(gòu)自動生成Hibernate映射文件,實體類和DAO,極大地減少了工作量。我們還對這個小例子進(jìn)行了壓力測試(測試時的數(shù)據(jù)量為10萬條記錄),確定平臺不存在性能問題。
通過這個實例我們把整個架構(gòu)基本走通一遍,并總結(jié)了使用這套架構(gòu)開發(fā)時適用的開發(fā)流程和需要做的工作。
3、困擾我們的問題
在實現(xiàn)例子和現(xiàn)在的項目應(yīng)用過程中,我們發(fā)現(xiàn)了若干頭疼的問題,有的解決了,有的還沒有。
問題1:要不要使用DTO?
在上面的架構(gòu)中我們并沒有明確Service和Web層間的數(shù)據(jù)傳輸是如何進(jìn)行的。我們討論好久要不要使用DTO,最后的結(jié)論是不用。
使用DTO有兩個主要的理由:1、減少Web層和Service層間的方法調(diào)用,通過一個方法調(diào)用就將Web需要的數(shù)據(jù)都傳給Web。2、隔離domain model和Web層。
第一個理由在我們的架構(gòu)下是不成立的。因為我們的架構(gòu)是集中式的,Web和Service是在同一個JVM中,它們之間的方法調(diào)用是沒有EJB遠(yuǎn)程訪問的巨大消耗的。
第二個理由還是需要考慮的。如果允許把domain model中的對象傳給Web層,那么修改domain model,就會影響到Web層。如果使用DTO,那么domain model實現(xiàn)上的變化就不會影響到Web。但是大量的變化不是domain model實現(xiàn)上的變化,而是domain model接口的變化,比如一個domain model的對象上添加了一個屬性,而這個屬性需要用戶修改,那么這時候必須修改Web層,不管是不是用了DTO。而且使用DTO,就需要維護(hù)著一大堆對象,或是它們的生成器,這是非常無聊、且容易出錯的工作。
基于這些考慮,我們沒有使用DTO,而是選擇把domain model直接傳到Web層。下面是修改后的架構(gòu)圖(呵呵,修改了別人的圖[2])。
圖2:
問題2:Entity like domain model or rich domain model?
我們使用Middlegen自動生成Hibernate映射文件,Entity類和DAO類, 但是生成的Entity只含有簡單的屬性和getter, setter方法。因此我們遇到了一個問題:我們的domain model還要不要包含domain logic?如果包含,那么和自動生成工具如何結(jié)合?
我們討論后認(rèn)為一個rich domain model還是非常有必要的,可以減少Service中的重復(fù)代碼,提高復(fù)用性。
如何同自動生成結(jié)合?使用<meta>標(biāo)簽,生成抽象基類,我們繼承這些自動生成的基類,添加業(yè)務(wù)方法。
問題3:Model driven or Data driven?
采用Model driven還是Data driven的方式大家有過熱烈的討論。我們主要是受到Rod Johnson[3]的影響,采用了Data driven的方式。先作數(shù)據(jù)庫設(shè)計,生成庫表,然后用Middlegen反向生成Hibernate映射文件和Entity及DAO。但是我們在進(jìn)入項目應(yīng)用之后發(fā)現(xiàn)這種方法有兩個問題:
a) 數(shù)據(jù)庫設(shè)計僅說明了系統(tǒng)要管理的靜態(tài)數(shù)據(jù),我們還是得作面向?qū)ο蠓治觯苑从诚到y(tǒng)的動態(tài)行為。特別是當(dāng)系統(tǒng)的業(yè)務(wù)不僅僅是簡單的CRUD操作時,這個問題更嚴(yán)重。
b) 數(shù)據(jù)庫設(shè)計為了優(yōu)化性能,可能會把好幾個應(yīng)該是單獨實體的數(shù)據(jù)放入一個實體中。這樣如果直接把這種極粗粒度實體映射成Entity類,那簡直是不可接受的。面向?qū)ο蟮姆治鲈O(shè)計模型得到的類都是相當(dāng)細(xì)粒度的。這種情況還得作面向?qū)ο蟮姆治觯鞔_到底這個粗粒度的大表應(yīng)該映射成那幾個細(xì)粒度的對象。
或許我們應(yīng)該試試Model driven,用AndroMDA生成domain model,Hibernate DAO,Hibernate mapping,數(shù)據(jù)庫表,簡單的Service和前臺的Tapestry頁面。
問題4:Hibernate Session生命周期如何管理?
對于Hibernate Session的生命周期我們采用的是Session-per-Transaction模式,未采用Open Session in View模式。 雖然Hibernate team認(rèn)為這種方法沒什么不好,而且FreeRoller和Atlassian的confluence都使用了Open Session In View這種模式,但是我們對它可能產(chǎn)生的影響還沒有很好的把握,所以暫時棄置不用。 如果Web層要訪問lazy load的數(shù)據(jù), 需要先調(diào)用Service的業(yè)務(wù)方法, 以獲得數(shù)據(jù).
問題5:Use case logic 和domain logic 如何區(qū)分?
Service負(fù)責(zé)use case logic,domain model負(fù)責(zé)domain logic。這樣的劃分看起來很好,實現(xiàn)起來就很麻煩。如何確定什么是use case logic,什么是domain logic?TBD.
問題6:Service粒度如何確定?
這個問題真是很煩,原先考慮使用usecase controller的方式,每個usecase對應(yīng)一個Service,但是發(fā)現(xiàn)這樣復(fù)用性太低,而且好多地方必須復(fù)用相同的功能
另一種方法是用package level service,每個package作個service,這樣倒是可以重用,但是感覺太死了,不好。
現(xiàn)在也沒有什么很好的辦法,只好在詳細(xì)設(shè)計時根據(jù)具體情況確定需要多少個Service了。TBD.
問題7:權(quán)限如何設(shè)定?如何檢查?
權(quán)限設(shè)定也是個頭疼的問題。我們本想是按照use case設(shè)定權(quán)限,每個用例一個權(quán)限。在角色設(shè)定的時候直接處理的都是業(yè)務(wù)意義非常明確的權(quán)限。但是在權(quán)限驗證過程中發(fā)現(xiàn)了問題:如果在Service的方法中驗證權(quán)限,而且這個方法在多個用例中用到(復(fù)用Service),那么這個Service的方法就需要檢查多個權(quán)限; 如果每個Service方法對應(yīng)一個權(quán)限, 那么權(quán)限又太細(xì)了, 不像use case權(quán)限那樣代表一個完整的業(yè)務(wù). 真的是很麻煩阿!TBD.
Ok, 這些就是我們這段工作的結(jié)果。希望能給大家一些啟發(fā),也希望能得到大家的幫助,幫我們出出主意,謝謝大家。
posted on 2008-10-04 23:26
Blog of JoJo 閱讀(380)
評論(1) 編輯 收藏 所屬分類:
Programming 相關(guān)