我們在做企業級業務系統的開發時,首選的解決方案是領域邏輯+對象關係表映射。什麼是企業級業務系統呢?第一,針對特定的領域,邏輯相對複雜。第二,涉及到資料的持久化,有很多非技術的原因,一般都會選擇關聯式資料庫。第三,有一些工作流程和豐富的使用者互動。
在選擇分層的時候,我們的首選方案也是選擇之前所說的DDD分層。UI層->領域層->基礎設施層。在邏輯層上是這麼一個分法,但在實際實施的時候,物理上代碼應該怎麼布置,肯定也是有一定的講究的。
首先,說一下核心邏輯層,這個層自然至少要有一個程式集的,一般的應用一個程式集就足夠了。在分層的時候還有一個領網域服務層,也可以放置到這一個程式集中。這裡面既可以定義領域模型,又可以定義工作流程。
第二,基礎設施層如何組織。基礎設施層是幹什麼的呢?是為上層的領域邏輯和工作流程的實現提供具體的技術支撐。和物理磁碟的互動(讀XML檔案,訪問資料庫),和外部系統的互動(調用外部系統的WebService,發送郵件的具體技術實現代碼)等等,都應該屬於這個範疇。但在這些主要的基礎設施的範疇中,有一種是比較特殊的,就是和關聯式資料庫的互動,即我們經常說的資料庫訪問層。一般的在實現這部分時都會使用對象關係表映射器(例如Nhiberate)。這部分應該要至少提出一個程式集來。其餘的基礎設施可以另外放到一個程式集中。
第三,有了領域模型,業務流和支撐這些邏輯啟動並執行具體技術,怎麼讓這些東西結合起來?應用服務層就是這個作用。這裡需要單獨提出一個程式集。應用服務層的作用就是領域模型,業務流和底層支撐技術結合起來,為上層的UI操作提供一個清晰的,大粒度的API。這種方式在實際開發中很有效,這樣大大減輕了UI層和商務邏輯的耦合,使UI層能夠方面的調用清晰的應用服務API外,就可以讓他自己關注自己的顯示邏輯,這才是UI層本身的職責。對於應用服務層,我很尊敬的大師(我是看他們的書正確認識了什麼是系統設計)說這個層是一個很簡單的層。但我沒有這樣的體會,雖然從領域業務上來看,這個層所包含的業務是簡單的,但他對於如何組織一次操作流程和操作單元式很重要的。
第四,UI層。這個層肯定也是需要至少一個程式集的。主要負責實現UI顯示邏輯。首先要注重自訂控制項和自訂群組件的使用。自訂的控制項和組件式UI層封裝和複用的基礎。我自己而言也曾經走過一些彎路,從不用自訂控制項,到儘可能的多用,又到能夠很好的區分什麼樣的UI邏輯應該封裝成自訂控制項,以及封裝的粒度有多大。對自訂控制項的感覺也是又覺得好用,到不好用,後來發現了自己覺得不好用的原因,進行改進,發現特別有用。
為什麼我在UI層花費那麼多篇幅呢?因為這個地方是平常開發最容易產生混亂代碼的地方。因為對於剛開始編程和經驗較少的開發人員來說,UI層太容易摻雜著商務邏輯了。這會導致邏輯容易混亂,代碼重複,複用性差的缺點。這個層也是我在公司推廣這種架構時現在遇到的最大阻礙。我自己也是在實際的使用過程中不斷的總結體會。
總之,做好UI層,我感覺有兩點要把握住。第一,不要摻雜商務邏輯,只讓其負責UI顯示。這方面可以考慮如果把當前的介面替換成命令列介面,是不是需要重複寫一些代碼,如果需要重複寫一些代碼,那就要考慮把這些代碼提到應用服務層或是領域邏輯層中。第二,讓客戶代碼調用這些UI或是對話方塊時,能以邏輯的方式去調用,不考慮具體的UI,這樣說明UI的封裝的不錯了。例如,一個角色列表的下拉框,在實現時,我可能會繼承ComboBox,然後再添加兩個方法和一個屬性,ShowAllRoles(),ShowRoles(ICollection<Role> pRoles),SelectRole{get;set;},這樣用該UI的客戶代碼在使用該UI的時候就不用關心該UI的任何顯示資訊,只需要知道該UI幹什麼用的,用就可以了。雖然說是這麼說,但真正在實際開發的時候,會遇到很多具體的問題。
第五,主應用程式,在這裡定義主表單,定義菜單。在大的業務系統中,菜單決不能直接用IDE加上去,要不主表單的代碼會很多,一些許可權也不好控制。還有登陸表單,以及實現整個系統的Application等。
第六,最後一個就是整個系統的FramWork了。裡面會定義一些介面,肯定會有IApplication,這個就和Word,Excel,ArcMap等大型系統中的Application一樣,是整個系統的全域變數,是貫穿整個業務系統生命週期的一個主介面。這個裡面要定義進度介面,當前系統的狀態等資訊。而實現這些介面的代碼會在主應用程式中火石應用服務中。因為有一些資訊要通過IApplication向基礎設施層,領域層或是其他層傳遞。但一些實現,例如顯示當前過程的進度資訊,就需要依賴主表單的實現,到主應用程式中實現。這就是介面和實現分離的一個很好的體現。之前我們所說的兩種好的解決耦合的方法,一個是介面和實現分離(這在ArcEngine中很多體現),另一個就是事件。
第七,在系統中,可能還有一些臨時的通用的代碼,可能這些代碼在這個項目中才提取成通用的,還沒有來得及歸納到公司的公用庫中,可能就需要這樣的一個叫Common程式集。等項目進行到尾聲,或是閒置時候,就需要考慮把這些代碼提取到公司的公用庫中。