領域驅動設計和開發(fā)實戰(zhàn)_第1頁
領域驅動設計和開發(fā)實戰(zhàn)_第2頁
領域驅動設計和開發(fā)實戰(zhàn)_第3頁
領域驅動設計和開發(fā)實戰(zhàn)_第4頁
領域驅動設計和開發(fā)實戰(zhàn)_第5頁
已閱讀5頁,還剩12頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、領域驅動設計和開發(fā)實戰(zhàn)背景領域驅動設計DDD)的中心內容是如何將業(yè)務領域概念映射到軟件工件中。大部 分關丁此主題的著作和文章都以Eric Evans的書 領域驅動設計為基礎,主要從概 念和設計的角度探討領域建模和設計情況。這些著作討論實體、值對象、服務等 DDD的主要內容,或者談論通用語言、界定的上下文Bounded Context)和咖層Anti-Corruption Layer )這些的概念。本文旨在從實踐的角度探討領域建模和設計,涉及如何著手處理領域模型并實際 地實現(xiàn)它。我們將著眼丁技術主管和架極帥在實現(xiàn)過程中能用到的指 導方針、最佳 實踐、框架及工具。領域驅動設計和開發(fā)也受一些架極、設

2、計、實現(xiàn)方面的影響,比 如:業(yè)務規(guī)則持久化緩存 事務管理安全代碼生成測試驅動開發(fā) 重構本文討論這些不同的因素在 項目實施的整個生命周期中怎 樣對其產生影響,還有 架極帥在實現(xiàn)成功的DDD中應該去尋求什么。我會先列出領域模型應該具備的典 型特征,以及何時在企業(yè)中使用領域模型3四丁根本不使用領域模型,或使用貧 血的領域模型來說)。文章包括一個貸款處理示例應用,來演示如何將設計立場、以及這里討論的開發(fā)最 佳實踐,應用在真實的領域驅動開發(fā)項目之中。示例應用用了一些框架去 實 現(xiàn)貸 款 處理領域模型,比如 Spring、Dozer、Spring Security、JAXB、Arid POJOs 和 Sp

3、ring Dynamic Modules示例代碼用Java編寫,侶對大多數(shù)開發(fā)人員來說,不論語 言背景如何,代碼都是很容易理解的。引言領域模型帶來了一些好處,其中有: 有助于團隊創(chuàng) 建一個業(yè)務部門與IT部門都能理解的通用模型,并用 該模型來溝通 業(yè) 務需求、數(shù)據(jù) 實體、過程模型。 模型是模塊化、可擴展、易于維護的,同時設計還反映了業(yè)務模型。 提高了業(yè)務領域對象的可重用性和可 測性。反過來,如果IT團隊在開發(fā)大中型企業(yè)軟件應用時不遵循領域模型方法,我們看看 會發(fā)生些什么。不投放資源去建立和開發(fā)領域模型,會導致應用架極出現(xiàn)“肥月盼層”和貧血的領 域模型”,假樣的架極中,外觀類(通常是無狀態(tài)會話Be

4、an)開始 積聚越 來越多 的業(yè)務邏輯,域對象則成為只有getter和setter方法的數(shù)據(jù) 載體。這種做法還 會導致領域特定業(yè)務邏輯和規(guī)則散布丁多個的外觀類中(有些情況下還會出現(xiàn)重 復的邏輯)。在大多數(shù)情況下,貧血的領域模型沒有成本效益;它們不會給公司帶來超越其它公 司的競爭優(yōu)勢,電在這種架極里要實現(xiàn)業(yè)務需求變更,開發(fā)并部署到生產環(huán)境中 去要花費太長的時間。在考慮DDD實現(xiàn)的項目中各種架極和設計因素之前,讓我們先看看富領域模型的 特性:領域模型應該側重于具體的業(yè)務操作領域。它應該結合業(yè)務模型、策略和 業(yè)務流程。它應該與業(yè)務中的其它領域,還有應用架構中的其它層隔離開來。它應該可重用,以避免相同

5、的核心 業(yè)務領域元素有任何重 復的模型和實現(xiàn)。模型應該設計得與應用中的其它層松耦合,這意味著領域層與上下兩層(即數(shù)據(jù)庫和 外觀類)都沒有依賴關系。它應當是一個抽象的、清晰劃分的 層次,以使 維護、測試、版本處理更容易??稍谌?器外(從IDE中)對領域類進行單元測試。它應該用POJO編程模型來 設計,沒有任何技 術或框架依 賴性(我總是告訴公司里我 工作的項目團隊,我們軟件開發(fā)用的技術是Java )。領域模型應該獨立于持久化實現(xiàn)的細節(jié)(盡管技術確實會對模型有一些限制)。它應該最小程度地依 賴于任何基 礎設施框架,因 為它將比這些框架更 經久,我們也不 希望與任何外部框架緊耦合。為了實現(xiàn)軟件開發(fā)中

6、更高的投資回報率ROI)業(yè)務單位和IT的高級管理人員必須 在業(yè)務領域建模及其實現(xiàn)的投資上 時間、做和資源)全力以赴。讓我們來看看實 現(xiàn)領域模型需要的其它因素。團隊應該經 常接近業(yè)務領域主題專家。 IT團隊(建模者、架構 師和開發(fā)人員)應具備良好的建模、設計技能。分析師應該具有良好的 業(yè)務流程建模技能。 架構師和開發(fā)人員應該有豐富的面向 對象設計(OOD)和編程(OOP )經驗。領域驅動設計 在企業(yè)架構中的作用領域建模和DDD在企業(yè)架極EA)核揮著重要的作用。因為EA的目標之一就是 結合IT和業(yè)務部門,業(yè)務實體的代表一一 域模型就是EA的核心部分。這就是為什 么大多數(shù)EA組件 業(yè)務或基礎設施)應

7、該圍繞領域模型設計和實現(xiàn)的原因。領域驅動設計和SOA面向服務的體系架極(SOA)最近幫助團隊極建基丁業(yè)務流程的軟件極件和服務、 加速新產品上市時間的勢頭越來越強勁。領域驅動設計是SOA的一個關鍵因素, 因為它有助丁封裝領域對象中的業(yè)務邏輯和規(guī)則。領域模型也提供了定 義服務契 約使用的語言和上下文。如果還沒有領域模型,SOA的實行就應該包括領域模型的設計和實現(xiàn)。如果我們太 過強調SOA服務、忽略了領域模型的重要性,那我們在應用架極中最終得到的就 是一個貧血的領域模型和臃腫的服務。理想的情況是,在開發(fā)應用層和SOA組件的同時,迭代地實現(xiàn)DDD,因為應用層和 SOA組件都是領域模型要素的直接消 費者

8、。使用豐富的領域實現(xiàn),通過給領 域對 象提供一個殼(代理),SOA設計將變得相對簡單。但如果我們太過丁關注SOA層, 在后端卻沒有一個像樣的領域模型,業(yè)務服務就會調用不完整的領域模 型,這可 能會導致出現(xiàn)一個脆弱的SOA架極。項目管理領域建模項目通常包括以下 步驟:首先為業(yè)務流程建模并文檔化。選擇一個候選的業(yè)務流程,與 業(yè)務領域專家一起使用通用 語言來文檔化 業(yè)務流程。識別候選業(yè)務流程需要的所有服 務。這些服務本質上可以是原子的(單步的)或組合 好的(多步的,有無工作流皆可)。它們也可以是業(yè)務(比如承?;?資金)或基礎設施 (比如電子郵件或工作調度)。.對上一步識別的服務所使用的對象,確定并文

9、檔化其狀 態(tài)和行為。一開始關注業(yè)務領域核心元素的時候,就將模型保持在高水平是非常重要的。從項目管理的觀點來看,真實的DDD實現(xiàn)項目和其它軟件開發(fā)項目所包含的階段 是一樣的。這些階段包括:對領域進行建模設計開發(fā)單元測試和集成測試基于設計和開發(fā)來完善、重構 領域模型(模型概念的持 續(xù)集成(CI)。 使用更新的 領域模型重 復上述步驟(領域實現(xiàn)的CI )。非常適合在這里使用敏捷軟件開發(fā)方法學,因為敏捷方法注重丁交付商 業(yè)價值,恰 好DDD側重丁結合軟件系統(tǒng)和業(yè)務模型。此外,就DDD迭代的特性來 說,SCRUM 或DSDM這樣的敏捷方法對項目管理來說也是更好的框架。結合使用SCRUM (適 用丁項目管

10、理)和XP (適用"件開發(fā)目標)方痢處理DDD實現(xiàn)項目來說非常 好。DDD迭代周期的項目管理模型如圖1所示。DDD lirplemefitatiQn CycleIUD.圖1. DDD迭代周期圖(庶擊查看大圖)領域建模結束時可以開始領域驅動設計。關丁如何開始實現(xiàn)領域對象模型, Ramnivas Laddad推薦如下的步驟。他強調要更側重丁領域模型中的領域對象,而 不是服務。 從領域實體和領域邏輯開始。不要一開始就從服務層開始,只添加那些 邏輯不屬于任何 領域實體或值對象的服務。利用通用語言、契約式設計(DbC )、自動化測試、CI和重構,使實現(xiàn)盡可能地與領 域模型緊密結合。從設計和實現(xiàn)

11、的角度來看,典型的DDD框架應該支持以下特征。 應該是一個以POJO (如果你的公司以.Net為主營,就是POCO )為基礎的架構。 應該支持使用DDD概念的業(yè)務領域模型的設計和實現(xiàn)。 應該支持像依 賴注入(DI)和面向方向 編程(AOP )這些概念的開箱即用。(注:稍 后將在文章中 詳細解釋這些概念)。. 與單元測試框架整合,比如 JUnit、TestNG、Unitils 等。 與其它Java/Java EE 框架進行良好的集成,比如 JPA、Hibernate、TopLink 等。示例應用本文中使用的示例 應用是一個住房貸款處理系統(tǒng),業(yè)務用例是批準住房貸款(抵 押)幽金申請。將貸款申請?zhí)峤?/p>

12、給抵押放貸公司的 時候,首先要通過承保過程, 承保人在這一過程中根據(jù)客戶的收入詳情、信用歷史記錄和其它因素來決定批準 還是拒絕貸款請求。如果貸款申請獲得承保組的批準,就進入貸款審批程序的結 活和融資步驟。貸款處理系統(tǒng)中的融資模塊自動給貸款人支付資金。通常,謎資過程從抵押放貸公 司(通常是銀行)彳偵款包遞交給產權公司開始。接著產權公司評估貸款包,并與房 產買賣雙方一起確定結活貸款的時間。貸款人和賣方與結算中介在產權公司會面、 簽署書面協(xié)議,來轉移房產產權。架構典型的企業(yè)應用架極由下面四個概念上的 層組成:用戶界面(表現(xiàn)層):負責給用戶展示信息,并解 釋用戶命令。 應用層:該層協(xié)調應用程序的活動。

13、不包括任何業(yè)務邏輯,不保存業(yè)務對象的狀態(tài), 但能保存應用程序任 務過程的狀態(tài)。領域層:這一層包括業(yè)務領域的信息。業(yè)務對象的狀態(tài)在這里保存。業(yè)務對象的持久 化和它們的狀態(tài)可能會委托給基礎設施層?;A設施層:對其它層來說,這一層是一個支持性的 庫。它提供 層之間的信息傳遞, 實現(xiàn)業(yè)務對 象的持久化,包含 對用戶界面層的支持性庫等。讓我們更詳細地看一下應用層和領域層。應用層:負責應用中UI屏幕之間的導航,以及與其它系 統(tǒng)應用層之間的交互。還能對用戶輸入的數(shù)據(jù)進行基本(非 業(yè)務相關)的驗證,然后再把數(shù)據(jù) 傳到應用的其 它層(更底層)。 不包含任何 業(yè)務、領域相關的邏輯、或數(shù)據(jù) 訪問邏輯。 沒有任何反映

14、商 業(yè)用例的狀 態(tài),但卻能 處理用戶會話或任務進展的狀態(tài)。領域層:負責業(yè)務領 域的概念,業(yè)務用例和業(yè)務規(guī)則的相關信息。領域對象封裝了業(yè)務實體的 狀態(tài)和行為。貸款處理應用中的業(yè)務實體例子有抵押(Mortgage )、房產(Property ) 和貸款人(Borrower )。如果用例跨越多個用 戶請求(比如貸款登記過程包含多個 步驟:用戶輸入貸款詳細信 息,系統(tǒng)基于貸款特性返回產品和利率,用戶選擇特定的產品/利率組合,最后系統(tǒng)會 用這個利率鎖定貸款),還可以管理業(yè)務用例的狀態(tài)(會話)。包含服務對象,這些服務對象只包含一個定 義好的、不屬于任何 領域對象的可操作行 為。服務封裝了業(yè)務領域的狀態(tài),而

15、業(yè)務領域并不適用于 領域對象本身。是商業(yè)應用的核心,應該與應用的其它層隔離開來。而且,它不 應該依賴于其它層使 用的應用框架(JSP/JSF、Struts、EJB、Hibernate 、XMLBeans 等)。卜面的圖2顯示了應用中使用的不同架極 層次,以及它們與DDD有怎樣的關系loan Applicaticn ArhiUrturc Diaq ram圖2.多層應用架極圖。燦查看大圖)下面的設計觀點被認為是目前DDD實現(xiàn)訣竅的主要部分: 面向對象編程(OOP) 依賴注入(DL) 面向方面編程(AOP )OOP是領域實現(xiàn)中最重要的基本原貝U。應該利用像繼承、封裝和多態(tài)這樣的OOP 概念,使用Pl

16、ain Java類和接口來設計領域對象。大部分領域元素是既有狀 態(tài)(屆 性)乂有行為(操作腿的方法或操作)的真正對象。它們同時對應丁真實世界的概 念,能很合 適地適用丁 OOP概念。DDD中的實體和值對象都是OOP概念的典型 例子,因為它們同時有狀態(tài)和行為。在典型的工作單元UOW中,領域對象需要與其它的對象協(xié)作,無論這些對象是服 務、資源庫、還是工廠。領域對象還需要處理其它那些本身就橫切的 關 注點,比 如領域狀態(tài)變化跟蹤、審計、緩存、事務管理(包括事務重試)。這些都是可重用、非 領域相關的關注點,通常很容易在包括 領域層的整個代碼中散布和重復。在領域 對象中嵌入該邏輯會導致領域層和非領域相關

17、的代碼互相糾纏、產生混亂。說到處理對象問之沒有緊耦合的代碼依賴關系和隔離橫切關注點的時候,OOP并 不能獨自為領域驅動設計和開發(fā)提供條好的設計解決方案。在這是可以利用DI和 AOP這樣的設計概念對OOP進行補充,以盡量減少緊耦合、提高模塊化、更好地處 理橫切關注點。依賴注入DI能很有效地將配置和依 賴代碼從領域對象中移出。此外,領域類對數(shù)據(jù)訪問對象 DAO)類、月嬌類對領域類的設計依賴性使得DI成為DDD實現(xiàn)中“瀕有”的內 容。顧i將資源庫和服務之類的其它對象注入到領域對象,di有助丁創(chuàng)建一個更活 晰、松耦合的設計。在示例應用中,服務對象FundingServiceImpD利用DI注入實體對象

18、Loan、 Borrower和FundingRequesD。實體也通過DI引用資源庫。IW羊的,像數(shù)據(jù)源、 Hibernate會話工廠和事務管理器這些其它的Java EE資源也被注入到服 務和資源 庫對象中。面向方面編程通過從領域對象中移除橫切關注點代碼,比如檢查、領域狀態(tài)變化跟蹤等,AOP有 助丁實現(xiàn)一個更好的設計(即右領域模型中少一些亂七八糟的內容)???利用 AOP把協(xié)同對象和服務注入領域對象,揪U是那些容器沒有 實例化的對象(比如持 久化對象)。在可以利用AOP的領域層中,其它的方面有緩存、事務管理和基 丁角 色的安全(授權)。貸款處理應用利用自定義方面將數(shù)據(jù)緩存引入服務對象。貸款產品

19、和利率信息從 數(shù)據(jù)庫表中加載一次(名戶端第一次請求這些信息時),然后御到適用丁后面產 品和利率查找的對象緩存JBossCache)中。產品和利率會被頻繁訪問、但不會定 期更新,所以緩存數(shù)據(jù)是一個很好的候 選方案,而不是每次都從后端的數(shù)據(jù) 庫獲 取。在近期的討論貼子里,DDD中DI和AOP概念的作用是主要的 話題。討論以 Ramnivas Laddad的演講為基礎,Ramnivas在其演講中主張,沒有AOP和DI的幫 助,DDD無法實現(xiàn)。Ramnivas在這個演講中討論了細粒度DI”的概念,這一概念 利用AOP使領域對象恢復機敏性。他說領域對象需要訪問其它細粒度的對象來提 供豐富的 行為,該問題

20、的解決方案是在領域對象中注入服務、工廠或資源庫(通寸 在調用極造或setter方法時期使用方面來注入依 賴)。Chris Richardson也討論了有關利用DI、對象和方面,通減少耦合、提高模塊化來 改進應用設計。Chris談到了 “嶇大服務”反模式,這是應用代碼耦合、混亂、分散 的結果,他還談了如何利用DI和AOP的概念來避免這一反模式。注解最近定義、處理方面和DI的趨勢是使用注解。對實現(xiàn)遠程服務(比如EJB或Web Services)初,注解有助丁減少所需的工件。它們還簡化了配置管理佚務。Spring 2.5、Hibernate 3,以及其它框架都充分利用注解在Java企業(yè)應用的不同層中

21、配置 組件。我們應該利用注解生成模板代碼,模板代碼能在靈活性上增加價值。但同時應該謹 慎使用注解。注解應該用丁不會引起混淆或 誤解實際代碼的地方。使用注解 的一 個 很好的例子是Hibernate ORM映射,注解能直接用類或屆性名給指定的SQL表 或列名添加值。另一方面,像JDBC驅動配置驅動類名、JDBC URL、用戶名和密 碼)這樣的詳細信息則更適合丁存放在XML文件中,而不是使用注解。這基丁數(shù)據(jù) 庫在同一個上下文中 這一假設。如頃域模型和數(shù)據(jù)庫表之 間需要相當多的轉 換,那就應該好好思考一下設計了。Java EE 5 提供 JPA 注解,t匕如£示四、PersistenceU

22、nit PersistenceContext 等 以此給簡單的Java類添加持久化細節(jié)。仲域建模上下文中,實體、資源庫和服務' 都是使用注解的好地方。©Configurable Spring將資源庫和服務注入領域對象的方式。Spring框架在 Configurable注解之上擴展了 領域對象依賴注入”思想。Ramnivas最近在博客中 談論了即將發(fā)布的Spring 2.5.2版本(從項目的Snapshot Build 379開始可用)的R新 改進。有三個新的方面(AnnotationBeanConfigurerAspect、AbstractInterfaceDrivenDep

23、endencyInjectionAspect 木日AbstractDependencyInjectionAspect)為領域對象依賴注入提供了簡單、更靈活的選 擇。Ramnivas說,弓I入中間的方 面AbstractInterfaceDrivenDependencyInjectionAspect ),其主要原因是領域特定的注解和接口發(fā)揮 作用。Spring還提供了其它注解來幫助 設計領域對象,比如 ©Repository Service和 Transactional示例應用中使用了部分注解。實體對象Loan> Borrower和FundingRequest使用了 Entity

24、注解;這些對象還使用©Configurable注解綁定資源庫對象;月盼類也使用 Transactional注解來用事務行為裝飾服務方法。領域模型和安全領域層的應用安全確保只有授 權的客戶端(/類用戶或其它應用)育朗用領域操 作,訪問領域狀態(tài)。Spring安全Spring Portfolio的一個子項目)同寸為應用的表現(xiàn)層(以URL為基礎) 和領域層(方提供爬田粒度的訪問控制。該框架使用Spring的Bean Proxy來 攔截方法調用,運用安全約束。它為使用MethodSecurityInterceptor類的Java對象 提供了基丁角色的聲明式安全。它也有 針對領域對象的訪問控制列

25、表(ACL's)形式 的實例級別安全,以控制實例級別的用戶訪問。在領域模型中使用Spring安全來處理授權需求的主要好處是,框架有一個非侵入 式的架極,我們可以完全隔離 領域和安全方面。此外,業(yè)務對象也不會和安全 實現(xiàn) 細節(jié)混成一團。我門可以只在一個地方 編寫通用的安全 規(guī)則,(使MOP技術)在 佚何需要實現(xiàn)它們的地方運用它們。在領域和服務類中,謎在類方法調用級別進行處理。舉例來說,對丁高達一白萬 美元的貸款,承保領域對象中的貸款審批”方法可以由佚何具有“承保人”角色 的用戶調用;頃丁超過一白萬美元的貸款申請來說,同ft域對象中的審批方法 則只能由具有“核保主管”角色的用戶調用。下表簡

26、要說明了應用架極每一層中應用的各種安全關注點 表1.各個應用層中的安全關注點層安全關注點客戶端/控制器認證、Web頁面(URL )界別授權外觀基于角色的授權領域領域實例級別授權、ACL數(shù)據(jù)庫DB對象級別授權(存儲過程、存儲函數(shù)、觸 發(fā)器)業(yè)務規(guī)則業(yè)務規(guī)則是業(yè)務領域中的重要部分。它們定義了數(shù)據(jù)驗證和其它的約束規(guī)則,這些規(guī)則需要應用丁特定業(yè)務流程場景中的領域對象。業(yè)務規(guī)則通常分為下面幾類:數(shù)據(jù)驗證數(shù)據(jù)轉換商業(yè)決策流程流向(工作流邏輯)上下文在DDD世界中非常重要。上下文的特性決定了 領域對象協(xié)作及其它運行時 因素,比如運用什么業(yè)務規(guī)則等。驗證以及其它業(yè)務規(guī)則往往都是在一個特 定的 業(yè) 務上下文中處

27、理的。這意味著,相同的領域對象在不同的業(yè)務上下文中將不得 不處理不同的一組業(yè)務規(guī)則。比如說,逾i 了貸款審批流程中的承保 步驟后,貸款 領域 對象的一些屆性(像貸款數(shù)額和利率)就不能再改變了。但在貸款剛剛登記并 與特定利率關聯(lián)的時候,翩羊的屆性是可以改 變的。盡管所有的領域特 定業(yè)務規(guī)則都應該封裝在領域層,但一些應用設計將規(guī)則放在 了外觀類中,這導致了領域類在業(yè)務規(guī)則邏輯方面變成了 貧血的”。在小本應用中 這可能是可接受的 解決方案,但不推薦將其用丁包含 復雜業(yè)務規(guī)則的中大型企業(yè) 應用。更好的設計方案是把規(guī)則放在它們應該在的地方一一 域對象中。如果一個 業(yè)務規(guī)則跨越兩個或兩個以上的實體對象,丹

28、隊該規(guī)則應該做為服務類的一部此外,如果我們不在應用中下苦功,往往把業(yè)務規(guī)則變成代碼里的一申switch語 句。隨著規(guī)則變得越來越復雜,開發(fā)人員不會愿意花費時間去重極代 碼,將 switch語句移到更易丁管理的 設計中。在類中硬編碼復雜的流向或決策規(guī)則邏輯 會導致類中出現(xiàn)更長的方法、代碼重復、蟀僵化的應用設計,長遠來看,這 將成 為維護的噩夢。一個良好的設計是把所有的規(guī)則(鎖U是隨著業(yè)務策略的變化而頻 繁改變的復雜規(guī)則)放至規(guī)則引擎(利用規(guī)則框架,比如JBoss Rules、OpenRules或 Mandarax)中去、并順域類中進行調用。驗證規(guī)則通常會用不同的 語言實現(xiàn),比如Javascrip

29、t、XML、Java代碼,還有其它腳 本語言。但由丁業(yè)務規(guī)則的動態(tài)特性,Ruby、Groovy、領域特定語言DSL)這些腳 本語言是定義、管些規(guī)則更好的選擇。Struts應用層)Spring (月路層)和 Hibernate ORM)都有其自己的驗證模塊,我們可以在這些驗證模塊中對傳入或傳 出的數(shù)據(jù)對象運用驗證規(guī)則。在一些情況下,驗證規(guī)則還能被處理為方面,它們可 以組合到應用的不同層次中去(比如服務和控 制器)。在編寫領域類處理業(yè)務規(guī)則時,緊記單元測試方面是非常重要的。規(guī)則邏輯中的佚 何變化都應該很容易、獨立地單元可測。示例應用包括一個業(yè)務規(guī)則集來驗證貸款特性是否都在允 許的產品和利率規(guī)格內。

30、規(guī)則在腳本語言中Groovy)進行定義,并用于傳遞給FundingService對象的貸 款數(shù)據(jù)。設計從設計的角度出發(fā),領域層應該有一個定義活晰的邊界,以避免來自非核心領域層 關注點的層的損壞,比如特定供應商的說明、數(shù)據(jù)過濾、轉換等。領域元素 應該設 計為正確地保存領域狀態(tài)和行為。不同的領域元素會基丁狀 態(tài)和行為進行不同的 結極化。下面的表2展示了領域元素及其包含的內容。表2.領域元素及其狀態(tài)和行為領域元素狀態(tài)/行為實體、值對象、聚合狀態(tài)和行為都有數(shù)據(jù)傳輸對象只有狀態(tài)服務、資源庫只有行為同時包含狀態(tài)(數(shù)據(jù))和彳為(操作)性體、值對象、聚合應該有定義活晰的狀態(tài)和 行為。同時,該行為不應該超出對象

31、邊界的范圍。實體應該在作用丁本地狀態(tài)的用 例中完成大部分工作。但它們不應該知道太多無關的概念。對那些封裝領域對象狀態(tài)所需要的屆性來 說,好的設計實踐是只包括這些屆性的 getter/setter方法。設計領域對象時,只為那些能改變的屆性提供setter方法。此 外,公有的極造函數(shù)應該只含有必需的屆性,而不是包含 領域類中所有的屆性。在大部分用例中,我們并不是真的要去直接改 變對象的狀態(tài)。所以,代替改變內部 狀態(tài)的做法是,創(chuàng)建一個帶有已改變狀態(tài)的新對象并返回該新對象。這種方法在這 些用例中就足 夠了,還能降低設計的復雜性。聚合類對調用者隱藏了協(xié)作類的用法。聚合類可用來封裝領域類中復雜的、有侵入

32、性的、狀態(tài)依賴的需求。支持DDD的設計模式有幾種有助丁領域驅動設計和開發(fā)的設計模式。下面是這些設計模式的列表: 領域對象(DO )數(shù)據(jù)傳輸對象(DTO ) DTO組裝器資源庫:資源庫包含領域為中心的方法,并使用 DAO與數(shù)據(jù)庫交互。 泛型DAO 時態(tài)模式(Temporal Patterns):這些模式給豐富的領域模型添加了 時間維。Bitemporal框架基于Martin Fowler的時態(tài)模式,為處理領域模型中的雙 時態(tài)問題 提供了設計方法。核心的 領域對象及其雙 時態(tài)屬性能用ORM產品持久化,比如 Hibernate 。在DDD中應用的其它設計模式還包括策略模式、外觀模式和工廠模式。Jim

33、my Nilsson在他的E里討論了工廠模式,認為它是一種領域模式。DDD反模式在最佳實踐和設計模式的反面,架極帥和開發(fā)人員在實現(xiàn)領域模型時還應該提防 一些DDD的壞氣味。由丁這些反模式,領域層在應用架極中成為最不重要的部分, 外觀類反而在模型中承擔了更重要的 責佚。下面是一些反模式: 貧血的領域對象 重復的DAO肥服務層:服務類在這里最終會包含所有的 業(yè)務邏輯。 依戀情結(Feature Envy):這是Martin Fowler 在他關于重構的 空中提到的典型的壞 氣味,在該反模式中,一個 類的方法對屬于其它類的數(shù)據(jù)太 過念念不忘。數(shù)據(jù)訪問對象DAO和資源庫在領域驅動設計中都很重要。DAO

34、是關系型數(shù)據(jù)庫和應用之間的契 約。它封裝了 Web應用中的數(shù)據(jù)庫CRUD操作細節(jié)。另一方面,資源庫是一個獨立 的抽象,它與DAO進行交互,并提供到領域模型的“業(yè)務接口” 。資源庫使用領域的通用語言,處理所有必要的DAO,并使用領域理解的語言提供 對領域模型的數(shù)據(jù)訪問服務。DAO方法是細粒度的,更接近數(shù)據(jù)庫,質源庫方法的粒度粗一些,而且更接近領 域。此外,一個資源庫類中能注入多個DAO。資源庫和DAO能防止解耦的領域模型 去處理數(shù)據(jù)訪問和持久化細節(jié)。領域對象應該只依賴丁資源庫接口。這就是為什么是注入資源庫、而不是DAO會 產生一個更規(guī)則的領域模型的原因。DAO類不能由客戶端(月盼和其它的消費者

35、類)直搠I用。名戶端應該始終調用領域對象,領域對象再調用DAO將數(shù)據(jù)持久化 到數(shù)據(jù)存儲中。處理領域對象之間的依賴關系(比女映體及其資源庫之間的依賴關系)虎開發(fā)人員 經常遇到的典型問題。解決這個問題通常的設計方案是讓服務類或外觀類直 接調 用 資源庫,仙用資源庫的時候返回實體對象給客戶端。該設計最終導致前面提 到的貧血領域模型,其中外觀類會開始堆積更多的業(yè)務邏輯,而領域對象則成為單 純的 數(shù)據(jù)載體。好的設計是利用DI和AOP技術將資源庫和服務注入到領域對象 中去。示例應用在實現(xiàn)貸款處理領域模型時遵循了這些設計原則。持久化持久化是一個基 礎設施方面,領域層應該與其解耦。JPA通過對類隱藏持久化實現(xiàn)

36、 的細節(jié),提供了這一抽象。它由注解推動,所以不需要XML映射文件。但同時,表 名和列名嵌在代碼中,在某些情況下可能并不是一個靈活的解決辦法。使用提供數(shù)據(jù)網(wǎng)格解決方案的網(wǎng)格 計算產品,比如Oracle的Coherence、 WebSphere的Object Grid、GigaSpaces,開發(fā)人員在建模和設計業(yè)務領 域時,完全 不需要考慮RDBMS。數(shù)據(jù)庫層用內存對象/數(shù)據(jù)網(wǎng)格的形式從 領域層抽象出來。緩存在我們討論領域層的狀態(tài)(數(shù)據(jù)歸寸,珂門不得不談到緩存問題。經常訪問的領域數(shù) 據(jù)(比如抵押貸款處理應用中的產品和利率)彳艮值得緩存起來。緩存能提高性能,減 少數(shù)據(jù)庫服務器的負載。月嬌層很適合緩存

37、領域狀態(tài)。TopLink和Hibernate這些 ORM框架也提供數(shù)據(jù)緩存。貸款處理示例應用使用JBossCache框架來緩存產品和利率詳情,以減少數(shù)據(jù)庫調 用、提高應用性能。事務管理對保持數(shù)據(jù)完整性、整體提交或回 滾UOW(工竹單元模式)加,事務管理是很重 要的。應該在應用架極層的哪里處理事務一直存在爭議。交叉實體的事務(在同一 UOW中跨越多個領域對象)也影響在哪里處理事務這一設計決策。一些開發(fā)人員傾向丁在DAO類中管理事務,這是一個欠佳的設計。該設計導致過 細粒度的事務控制,對那些事務跨越多個領域對象的用例來說,這種事務控 制沒 有 靈活性。服務類應該處理事務;即使事務跨越多個領域對象,

38、服務類也能處理事 務,電在大多數(shù)用例中,是服務類在處理控制流。示例應用中的FundingServiceImpl類處理資金申請的事務,過調用資源庫執(zhí)行多 個數(shù)據(jù)庫操作,并在單一事務中提交或回滾所有的數(shù)據(jù)庫變化。數(shù)據(jù)傳輸對象領域對象模型在結極上與從業(yè)務服務接收或發(fā)送的消息不茲容,在這樣一種SOA 環(huán)境中,DTO就是設計中很重要的一部分。消息通常都在XML模式定義 文檔XSD)中叔和維護,從XSD編寫(或伽生成)DTO對象,并在領域和SOA服務 層之間使用它們來傳輸數(shù)據(jù)(消息)是坤t1普遍的做法。在分布式應用 中,將來自 丁一個或多個領域對象中的數(shù)據(jù)映射到DTO中會成為必然的弊端,因為從性能和 安全

39、角度出發(fā),跨越網(wǎng)絡發(fā)送領域對象是不實際的。從DDD的角度來看,DTO還有利丁維護服務層和UI層之間的縫隙,其中DO用丁 領域層和服務層,DTO用丁表現(xiàn)層。Dozer框架用7將一或多個 領域對象組裝為一個DTO對象。它是雙向的,將領域對 象轉換為DTO的時候,它會保存大量備用的代碼和時限,反之亦然。DO和DTO之 間的雙向映射有利丁消除“ DCEU DTO和“DTCEU DO各自的轉換邏輯。該框架還 能正確處理類型和數(shù)組的轉換。示例應用在資金處理申請到來時,利用Dozer映射文件(XML)將FundingRequestDTO對象劃分成為 Loan、Borrower、FundingRequest實

40、體對象。在 返回給客戶端時,映射同樣負責將來自實體的資金響應數(shù)據(jù)聚合到單一的DTO對 象中。DDD實現(xiàn)框架像 Spring、Real Object Oriented ROO) Hibernate 和 Dozer 這些框架都有助丁 設計 并實現(xiàn)領域模型。支持DDD實現(xiàn)的其它框架有 Naked Objects、Ruby On Rails、 Grails,以及 Spring Modules XT Framework。SPig負責實例化,并將服務、工廠和資源庫這些領域類聯(lián)接在一起。它還使用 ©Configurable解將服務注入實體。該注解是Spring特有的,所以完成這一注入的 其它選擇是

41、使用諸如Hibernate攔截器的東西。ROO是建立在觀點 領域第一,基礎設施第二”之上的DDD實現(xiàn)框架。開發(fā)該框架 是為了減少Web應用開發(fā)中模式的模板編碼。利用ROO時,我門定義領域模型, 接著框架(基丁 Maven Archetype動為模型-視圖-控制器MVC)、DTO、業(yè)務層外觀 和DAO層生成代碼。它也能為單元測試和集成測試生成stubs。ROO有幾個非常實用的實現(xiàn)模式。比如說,它區(qū)分處理屆性的狀態(tài)、使用屆性級訪 問的持久層、只反映必需屆性的公有極造函數(shù)。開發(fā)沒有實際的實現(xiàn),模型就沒有用處。實現(xiàn)階段應該盡可能多地自動化完成開發(fā)佚 務。為了看看什么佚務能自動完成,讓我們看看涉及領域模

42、型的一個典型用例。下 面是用例的步驟列表:輸入請求: 客戶端調用外觀類,以XML文檔(XSD茲容的)的方式 發(fā)送數(shù)據(jù);外 觀類為UOW 初始化一個新的事務。 驗證輸入的數(shù)據(jù)。驗證包括基本 驗證(基本的/數(shù)據(jù)類型/屬性級檢查)和業(yè)務驗證。如 果有任何的 驗證錯誤,拋出適當?shù)漠惓?。將描述轉換為代碼(以成為簡單的領域)。改變數(shù)據(jù)格式,以成 為簡單的領域模型。進行所有的屬性分割(比如,在客 戶實體對象中,將客戶姓名分成名字和姓)。 把DTO拆分為一或多個 領域對象。持久化領域對象的狀態(tài)。輸出響應:從數(shù)據(jù)存儲中獲取領域對象的狀態(tài)。如果必要,緩存狀態(tài)。將領域對象組裝為對應用有利的數(shù)據(jù) 對象(DTO )。進

43、行所有的數(shù)據(jù)元素合并或分離(比如結合名字和姓,組成單一的客戶姓名屬性)。將代碼轉換為描述。必要時改變數(shù)據(jù)格式,以 處理客戶端數(shù)據(jù)使用的要求。如果有必要,緩存DTO的狀態(tài)。事務提交(如果有 錯誤則回滾),退出控制流。下表顯示了應用中不同的對象,這些對象將一個層的數(shù)據(jù)傳到另一個層。 表3.應用層間的數(shù)據(jù)流向1 層起點對象終點對象框架DAO數(shù)據(jù)庫表DOHibernate領域委托DODTODozer數(shù)據(jù)傳輸DTOXMLJAXB正如你所看到的,相同的數(shù)據(jù)以不同形式(DO、DTO、XML等)仙用架極中傳遞的 層并不多。大部分持有數(shù)據(jù)的這些對象Java或XML)還 有像DAO、DAOImpl、 DAOTes

44、t這些類實際上都是基礎設施。這些有模板代碼和結極的類、XML文件都很 適合代碼生成。代碼生成ROO這樣的框架還為新項目創(chuàng)建了一個標準、一致的項目模板(使用Maven插 件)。使順先生成的項目模板,我們可以實現(xiàn)目錄結極的一致性,其中存放源碼、 測試類、配置文件,以及對內部和外部(第三方)組件庫的依賴關系。典型的企業(yè)軟件應用所需的種種類和配置文件時,其數(shù)量之多令人望而生畏。代碼 生成是解決該問題的最好辦法。代碼生成工具通常使用某 類模板框架來定義模板, 或是代碼生成器能從中生成代 碼的映射。Eclipse建模框架EMD的幾個子項目有 助丁 Web應用項目需要的各種工件的代碼生成。模型驅動架極MDA

45、)工具,比如 AndroMDA,都利用EMF在架極模型的基 礎上生成代碼。說到在領域層編寫委托類,我看到開發(fā)人員手動編寫這些類(大多是從無到有地寫 完第一個,接著用發(fā)制并粘貼”的模式來為其它的領域對象創(chuàng)建所需的委托 類)。由驅些類大部分都是領域類的外觀,日門很適合代碼生成。代碼生成是長 遠的解決辦法,盡管建立并測試代碼生成器(引擎)增加了初期的投入(伽量和 時間)。對生成的測試類來說,一個好的選擇就是在需要進行單元測試的主類中,為帶有復 雜業(yè)務邏輯的方法創(chuàng)建抽象方法。這樣,開發(fā)人員能繼承生成的測試基類,然后實 現(xiàn)不能自動生成的自定義業(yè)務邏輯。RW,這個方法也適用丁佚何有不能自 動創(chuàng)建 測試邏輯

46、的測試方法。對編寫代碼生成器來說,腳列言是一個更好的選擇,因為它們開銷少,還支持模 板創(chuàng)建和自定義選項。如果我們在DDD項目中充分利用代 碼生成,我們只需要從 無到有地編寫少量的代碼。必須從無到有進行創(chuàng)建的工件有: XSD領域對象服務一旦我們定義了 XSD和Java類,我門可以生成下列全部或大部分的 類和配置文 件: DAO接口和實現(xiàn)類 工廠資源庫 領域代理(如果有必要) 外觀(包括 EJB和 WebService 類) DTO上述類的單元測試(包括測試類和測試數(shù)據(jù)) Spring配置文件表4列出了 Web應用架極中不同的層,以及那些層中能生成什么工件Java類或 XML文件)。表4. DDD

47、實現(xiàn)項目中的代碼生成層/功能模式你寫的代碼生成的代碼框架數(shù)據(jù)訪問DAO/資源庫DAO 接口,DAO實現(xiàn)類,DAOTest ,測試種子數(shù)據(jù)Unitils, DBUnit領域DO領域類DomainTest持久化ORM領域類ORM映射,ORM映射測試Hibernate, ORMUnit數(shù)據(jù)傳輸DTOXSDDTOJAXBDTO組裝組裝映射DO-DTO 映射文件Dozer委托業(yè)務委托DO到DTO的轉換代碼外觀外觀遠程服務,EJB,Web Service控制器MVC控制器映射文件Struts/Spring MVC表示層MVC視圖配置文件Spring MVC委托層是唯一同時理解領域對象和DTO的層。其它層,

48、例如持久層,不應該察覺到 DTO。重構重極就是改變或調整應用代碼,但不修改應用的功能或行為。重極可以是設計相關 的,也可以是代碼相關的。設計重極是為了不斷完善模型、重極代碼來提升領域模 型。由丁重極的迭代性和 領域建模不斷演進的性質,重極在DDD項目中發(fā)揮著重要作 用。將重極佚務集成到項目中的方法之一是在 項目的每次迭代中添加重極 環(huán)節(jié),重 極結束之后才算完成迭代。理想情況下,每項開發(fā)佚務之前和之后都應該進行重 極。進行重極應該有嚴格的規(guī)定。結合使用重極、CI和單元測試,以確保代碼變化不會 破壞佚何功能,同時,徹的變化要有助丁以后的代 碼和性能改進。自動化測試在重極應用代碼中發(fā)揮著至關重要的作

49、用。沒有良好的自動化測試和 測試驅動開發(fā)TDD)實踐,重極可能會產生反面的效果,因為沒有自動化的方式去 驗證作為重極一部分的設計和代碼并變化沒有改變行為、或破壞功能。像Eclipse這 樣的工具有助丁用迭代的方式和作 為開發(fā)一部分的重極來實現(xiàn)領域 模型。Eclipse有一些功能,比如把一個方法提取或移 動到不同的類中,或將一個方 法下推 到子類中。也有幾個Eclipse代碼分析插件有助丁處理代碼依賴關系、識別 DDD反模式。我做項目的設計和代碼審查時、都是依靠插件JDepend、Classycle和 Metrics來評估應用中領域和其它模 塊的質量。Chris Richardson談到運用代碼

50、重極、以使用Eclipse提供的重極功能將 過程設計轉 變?yōu)橐粋€OO設計。單元測試/持續(xù)集成我們剛才談到的目標之一是領域類應該(在最初的開發(fā)階段,以及隨后重極已有代 碼時)單元可測,而不過多依賴丁容器或其它基 礎設施代碼。TDD方法有 助丁團 隊盡早地找出佚何 設計問題,并有助丁驗證代碼與領域模型在保持一致。DDD對測 試先行開發(fā)來說是很理想的,因為狀態(tài)和行為都包含在領域類中,而且單獨測試 它們應該是容易的。測試領域模型的狀態(tài)和行為,乂不太過關注丁數(shù)據(jù)訪問或持久 化的實現(xiàn)細節(jié)是很重要的。單元測試框架,比如JUnit或TestNG,都是實現(xiàn)和處理領域模型很棒的工具。其它 測試框架,像DBUnit

51、和Unitils,也可用來測試領域層,尤其是把測試數(shù)據(jù)注入到 DAO類中。對在單元測試類中增加測試數(shù)據(jù)來說,這將大大減少編寫額外的代碼。 模擬對象Mock objects)向羊有利丁單獨測試領域對象。但是在領域層不要濫用模 擬對象是很重要的。如果有其他 測試領域類的簡單方法,你應該使用這些方法來代 替使用 模擬對象。比如說,如果你能使用真實的后端DAO類(而不是豐堀的DAO 實現(xiàn))和內存HSQL數(shù)據(jù)庫(而不是真實的數(shù)據(jù)庫)測試一個實體類,能使領域層單 元測試運行得更快,而運行得更快正好是使用模 擬對象潛在的主要想法。這樣,你 將能測試領域對象之間的協(xié)作(交互),以及窗之間交換的狀態(tài)(數(shù)據(jù))。使

52、用莫 擬對象,我們則只能測試領域對象之間的交互。一旦開發(fā)佚務完成,所有在開發(fā)階段創(chuàng)建的單元測試和集成測試(不管有沒有使用 TDD做法)都將以 自動化測試套件的一部分。這些測試用應該經常進行維護,并 經常在本地或更高一級的開發(fā)環(huán)境中執(zhí)行,以便找出新的代碼變化是否在領域類 中引入了 BugEric Evans在他的坐中提到了 CI,CI應該始終運用在界定的上下文中,應該包括人和代碼的同步。像CruiseControl和Hudson這些CI工具可用來建立一個自 動 化極建和測試的環(huán)境,來運行應用極建腳本(使用Ant或Maven這些極建工具創(chuàng)建) 從SCM倉庫中(像CVS、Subversion等)檢出

53、代碼,編譯領域類(以加用中的其它 類),并在沒有極建昔誤的情況下自動運行所有的測試 單元測試和集成測試)CI 工具還可以設置在有佚何極建或 測試錯誤時(迎E-mail或RSS Feeds)通矢咬目 團隊。部署領域模型絕對不會是靜態(tài)的;傾目生命周期中,它們會隨著業(yè)務需求的演變、新 項目中新需求的提出而 發(fā)生變化。此外,隨著你開發(fā)和實現(xiàn)領域模型,你能不斷學 習和提高,而且你也想在已有的模型中運用新的知 識。打包、部署領域類的時候,隔離很關鍵。電領域層依賴丁 DAO層的一面,而服務 外觀層乂依賴丁 DAO層的另一面(參見圖2-應用架極圖),所旌些領域類打包、 部署為一或多個模塊來處理依賴關系很有意義

54、。DI、AOP和工廠這些設計模式在設計階段減少了對象之間的耦合,并使應用模塊 化;OSGi (以前被稱為開放服務網(wǎng)關規(guī)范)則在運行時處理模塊化。OSG i正在成為 打包、發(fā)布企業(yè)應用的標準機制。它能很好地處理模塊之間的依賴關系。我們還能 用OSGi來進行領域模型的版本處理。我們可以把DAO類打包到一個OSGi的Bundle DAO Bundle)中,把月爵外觀類打 包到另一個Bundle (月盼Bundle)中,所KDAO或服務實現(xiàn)進行了修改,或是部署 了應用的不同版本,由丁 OSGi,應用都不需要重啟。如果我們?yōu)榱讼蚝笃澣?,必?支持某些領域對象已有的版本和新的版本,那我 們也可以部署相同領

55、域類的兩個 不同版本。為了利用OSGi的能力,應用對象在消費之前(即在客戶端能查找到它們之前),應 該在OSGi平臺中進行注冊。這意味著我們必須使用OSGi的API進行注冊,我們還 必須處理使用OSGi容器啟動和通知服務時的失敗場景。Spring Dynamic Modules 框架對該領域很有利,它允許在應用中導出或導入佚何對象類型,而不改變佚何代 碼OSpring DM還提供測試類,以在容器外運行OSGi集成測試。比如說,能從IDE中直 接用AbstractOsgiTests運行集成測試。設置由測試基礎設施來處理,所以我們不需 要為測試編寫MANIFEST.MF文件,或者進行佚何的打包或部署。該框架支持大部 分目前可用的 OSGi 實現(xiàn) Equinox、Knopflerfish 和 Apache Felix)。貸款處理應用使用OSGh Spring DM、Equinox容器來處理模塊級別的依賴關系,以 及領域和其它模 塊的部署。LoanAppDeploymen

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論