文字寫完以后,突然想起來很久以前在JavaEye上看的關于貧血模型、富血模型、充血模型的討論。最后的結論大概與持久化無關的業務邏輯應該放在模型中實現,而與持久化有關的邏輯要放到Service中實現。看這幾多文字,其實也就是說這么個意思,不過多說一些。閑話休提,正文開始。
在大結構上系統描述為上面的圖形。關鍵元素是
領域模型、
領域服務、
基礎服務。能夠精準的反應業務的模型,和圍繞模型的有效的服務,是軟件團隊在某一行業領域生產成果價值的兩個很重要的體現。領域服務作為系統的核心邏輯之一,一方面,承擔著完成所有與技術環境相關的業務邏輯的實現,另外一方面,作為領域模型和技術環境的緩沖,保證了領域模型的
普適、獨立。
作為獨立的價值體現,建模的結果需要有相當高的普適性。概念模型因為和實現無關所以具有先天的普適性,但到了實現層次,保證模型的獨立性就成了一個需要專門考慮的問題。通常的做法是對于一個業務邏輯,判斷它是否與環境相關,如果不是,那么放在模型中實現,如果是,則放在領域服務中實現。這樣保證領域模型的普適性,得到了可移植的最大價值。領域服務作為領域模型和基礎服務之間的一個緩沖層次,隔離領域模型和環境以及上下文。丟失領域模型的普適性可能是技術架構的問題導致的,也可能是建模過程的問題導致的。
在技術架構方面,一方面,我們要使模型盡可能的普適,那么就要求模型盡可能的少依賴環境。嚴格說來,模型應該完全不依賴與環境。例如我們將行為主體建模成為一個User模型,他有名稱,有密碼,但很明顯這一切與環境無關。不論你的技術架構是開源的技術棧,或者是公司自己的技術積累,這個模型應該是都是成立的。在主流的以開源技術棧上實現的應用中,一般都會有DAO(Data Access Object),Service等對象在POJO類周圍,完成領域服務,同時讓POJO模型僅從事與環境無關的業務實現。由此看來,所謂不依賴,和非侵入性有很大關系。在Hibernate中,以XML來描述映射關系元數據,保證了模型不被侵入(Hibernate3.2以后加入了在Class上面用Annotation配置映射源數據的方式,但因為Annotation本身是程序元數據而不是程序的常規內容,不影響業務邏輯實現,所以也可以看做是沒有侵入)。由此也可以看出另外一個方面,系統的技術架構為了能夠實現盡可能多的“通用服務”,需要在模型上加入許多的限制,這種限制在Hibernate中體現為:POJO要符合JPA規范的要求,同時,嚴格一點,應該符合JavaBeans規范。但是我們也看到,模型遵循這些規范并沒有給自己帶來侵入和對環境的依賴。所以我認為,實現通用服務所要求的對模型的限制,完全可以僅僅是規范的形式,而不是直接到實現層次。
在建模方面,準確的分辨一個業務邏輯單元是否依賴環境,將那些與環境無關的業務邏輯放在模型實現中,而將那些與環境緊密相連的業務邏輯放在服務中。原因也可以從兩個方面來說明,一個方面是許多業務的實現過程,本身就是領域對象所應該包含邏輯的表達。例如給一個用戶組增加用戶,我們會建模成為Group.add(User)。與服務無關。是為職責所定,不能亂放。另外一個方面,如前所述,領域模型與環境無關,而領域服務則依賴于環境,于是領域模型可以普適,輕易的移植,而領域服務則不然,這也將使我們實現業務邏輯的時候,應該默認的選擇領域對象,用業務邏輯的實現來使領域模型更加富有內涵,將使我們在不同的系統中達到最大限度的重用,包括代碼重用和業務邏輯重用。所以,良好的區分模型和服務的職責和邊界,是業務系統設計要解決的最重要的問題之一。
另外一個需要說明白的問題是依賴方向。Java企業及應用開發發展這么多年,追求系統各部分之間的松耦合這個方向一直沒有變。松耦合帶來的好處無需贅言。對于具體的實現方式,或者說軟件設計、實現中應該考慮、注意的問題,有很多的開源框架做了深入的探討。通用的模式是表現、領域服務、模型、基礎服務各個部分按照縱向排列,任意二者之間最多只能有一個方向的依賴,而且不相鄰層次之間不允許發生直接依賴關系。表現層依賴于模型和領域服務,領域服務依賴于模型和基礎服務,模型不對外界環境發生依賴,基礎服務也不可能對其上面的層次發生依賴。系統模塊確定的對外接口和單向依賴使得各部分之間的耦合程度降到最低,一個部分內部的變化不會影響到其他部分,對外接口的調整也僅影響到系統架構上更高的層次,由此提高系統的可擴展性、可維護性。從技術角度上講,一些細節的實現需要模型的實現者依賴環境。例如在實現延遲加載這個細節的時候,模型中必須存在對能夠獲取到數據的對象的引用,于是就造成了對環境的依賴。但是,這個情況的發生,應該在模型類的可見級別控制范圍內,或者從規范以及建議層面禁止直接調用。