作者:anders小明
2009年5月7日
需求背景
現(xiàn)在的樣子
如PoEAA中提到的:事務(wù)腳本(Transaction Script)和表模型(Table Moduel)模式。
存在問(wèn)題
事務(wù)腳本看到的是零散的數(shù)據(jù),而表模型混合了下文要說(shuō)領(lǐng)域模型和領(lǐng)域服務(wù)界限。
1. 兩者都導(dǎo)致了分析和設(shè)計(jì)的割裂,領(lǐng)域模型只存在于分析中;數(shù)據(jù)間的內(nèi)在關(guān)系無(wú)法通過(guò)代碼體現(xiàn);
2. 兩者都無(wú)法有效的實(shí)現(xiàn)業(yè)務(wù)邏輯的具體的差異化和抽象的一致性;導(dǎo)致學(xué)習(xí)和維護(hù)成本的增加;
3. 兩者都帶來(lái)了測(cè)試上困難,增加了開(kāi)發(fā)成本;
為何要面向?qū)ο螅?/span>
1. 面向?qū)ο笫亲陨隙碌拈_(kāi)發(fā)方法;這種方式對(duì)于迭代增進(jìn)式的結(jié)構(gòu)化過(guò)程來(lái)說(shuō)成本是最低的;
2. 數(shù)據(jù)和業(yè)務(wù)邏輯的綁定性,采用面向?qū)ο笕菀撞僮骱蜏贤ǎ磳?shí)施成本低),同時(shí)有助于區(qū)分狀態(tài)邏輯和任務(wù)邏輯;
3. 業(yè)務(wù)邏輯的差異性,同一個(gè)業(yè)務(wù)邏輯針對(duì)差異性數(shù)據(jù)有區(qū)分做法,采用面向?qū)ο笕菀拙S護(hù);
什么是領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的提出是由Eric Evans在其《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》一書(shū)提出。實(shí)質(zhì)上是一種由內(nèi)而外的設(shè)計(jì)方法,俗話(huà)說(shuō)的先中間(模型和服務(wù))后兩邊(界面,數(shù)據(jù)庫(kù)以及集成)。
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的優(yōu)勢(shì)
傳統(tǒng)的開(kāi)發(fā)方式:基于數(shù)據(jù)庫(kù)的設(shè)計(jì)開(kāi)發(fā)。數(shù)據(jù)庫(kù)提供的設(shè)計(jì)模型是表和字段兩種粒度,這兩種粒度有時(shí)并不合適于系統(tǒng)設(shè)計(jì):
1. 模型的結(jié)構(gòu)化能力
1.1. 同一組件下的設(shè)計(jì)優(yōu)勢(shì),一個(gè)模型可以來(lái)自多張表的數(shù)據(jù)聚合而成,一張表可以聚合多個(gè)模型;一個(gè)邏輯是由幾個(gè)固定字段或者非固定字段聚合;模型間的關(guān)聯(lián)關(guān)系也是使用表無(wú)法展示的——外鍵的約束對(duì)于系統(tǒng)開(kāi)發(fā)來(lái)說(shuō)實(shí)在太有限了。而這些不論表還是字段粒度都無(wú)法支持的。
這里強(qiáng)調(diào)一下模型間的關(guān)聯(lián)關(guān)系,特別是和生命周期相關(guān)的聚合和組合關(guān)系。關(guān)系數(shù)據(jù)庫(kù)中所保存的是系統(tǒng)分解后的表示,即關(guān)系被分解了而不是被表達(dá)了,外鍵對(duì)數(shù)據(jù)關(guān)系只起到一種約束作用,它對(duì)于查詢(xún)語(yǔ)句的構(gòu)建并沒(méi)有直接的影響。所有數(shù)據(jù)之間的關(guān)系都必須在查詢(xún)的時(shí)候明確指定出來(lái),即調(diào)用者必須擁有數(shù)據(jù)關(guān)系的知識(shí),而不是數(shù)據(jù)本身?yè)碛羞@些知識(shí).在如下的SQL語(yǔ)句中
“select * from a, b where a.fldA = b.fldB and a.fldC = 1 and b.fldD = 2”
其中,a.fldA = b.fldB 可以稱(chēng)為關(guān)聯(lián)條件,而a.fldC=1可以稱(chēng)作是坐標(biāo)條件。
SQL的復(fù)雜性很大程度上來(lái)源于我們頻繁的需要在各處指定完全一樣的關(guān)聯(lián)條件而無(wú)法把它們抽象成可復(fù)用的組分.在ORM所提供的對(duì)象空間中,對(duì)象之間的兩兩關(guān)聯(lián)只要指定一次,就可以在增刪改查等各種操作過(guò)程中起到作用,特別是在對(duì)象查詢(xún)語(yǔ)句中,可以通過(guò)兩兩關(guān)聯(lián)自動(dòng)推導(dǎo)出多實(shí)體之間的關(guān)聯(lián)關(guān)系,雖然推導(dǎo)出的結(jié)果未必是最優(yōu)化的.
1.2. 采用模型方式容易解決項(xiàng)目的集成問(wèn)題(兩個(gè)組件訪問(wèn)同一張表的情況)
2. 架構(gòu)的結(jié)構(gòu)化能力
事務(wù)腳本直接訪問(wèn)到表。換句話(huà)說(shuō),其架構(gòu)只有服務(wù)+數(shù)據(jù)庫(kù)表,這樣的架構(gòu)下數(shù)據(jù)庫(kù)表可以說(shuō)就是我們的模型。
這里看看在邏輯設(shè)計(jì)上,領(lǐng)域模型驅(qū)動(dòng)設(shè)計(jì)對(duì)于架構(gòu)影響
A. 服務(wù), 模型和模型倉(cāng)庫(kù)(Repository),模型的重建和關(guān)聯(lián)交由模型倉(cāng)庫(kù)完成,單個(gè)數(shù)據(jù)邏輯交給模型處理(支持泛化);
B. 測(cè)試的好處;
3. 分析和設(shè)計(jì)的統(tǒng)一
溝通的問(wèn)題,客戶(hù)關(guān)注于其業(yè)務(wù),分析的模型接近于客戶(hù)需求,設(shè)計(jì)也采用模型的方式,避免分析和設(shè)計(jì)的割裂。有助于開(kāi)發(fā)人員間,開(kāi)發(fā)人員和業(yè)務(wù)人員以及客戶(hù)間的溝通
4. 其它好處
4.1.采用領(lǐng)域模型可以屏蔽持久化信息。持久化設(shè)計(jì)的表設(shè)計(jì)是和DBA相關(guān)的,DBA對(duì)于表設(shè)計(jì)有權(quán)力的。采用模型可以有效隔離各自工作;
4.2. 模型可以通過(guò)很多的手段透明的解決性能問(wèn)題,而采用表做模型導(dǎo)致容易導(dǎo)致性能問(wèn)題,當(dāng)然不是沒(méi)有辦法解決,一種是通過(guò)DataSet的方式,但這樣的切換成本較高。
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的可能
上下文環(huán)境
領(lǐng)域模型存在于系統(tǒng)的各個(gè)地方,不過(guò)在不同地方有不同的映射實(shí)現(xiàn)。在通常的開(kāi)發(fā)過(guò)程中,該映射存在于文檔和開(kāi)發(fā)人員的腦海中的。當(dāng)要向客戶(hù)展示時(shí),就面臨一個(gè)映射的關(guān)系。比如要允許客戶(hù)可以在線編輯頁(yè)面呈現(xiàn)的顯示元素,在規(guī)則定義里使用對(duì)象系統(tǒng)時(shí)。
基礎(chǔ)平臺(tái)
隨著ORM框架的發(fā)展,如hibernate,可以提供繼承多態(tài)等能力,能夠支持關(guān)聯(lián)關(guān)系,特別是面向?qū)ο笤O(shè)計(jì)中生命周期相關(guān)的聚合關(guān)系,使得基于領(lǐng)域模型開(kāi)發(fā)在技術(shù)上具備了可行性。
當(dāng)前不足
不過(guò)ORM在集合上的處理不盡如人意。受限于JDK的集合,不能像DLinq那樣提供集合的過(guò)濾和集合函數(shù)操作。使得一些設(shè)計(jì),不得不屈從于性能問(wèn)題,把領(lǐng)域模型的關(guān)聯(lián)關(guān)系人為斷開(kāi)。
Ferrum項(xiàng)目或許會(huì)是一個(gè)可行的方案。
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的技術(shù)分析
概述
整體結(jié)構(gòu)
領(lǐng)域分兩個(gè)含義:領(lǐng)域模型,領(lǐng)域服務(wù),業(yè)務(wù)規(guī)則和Repository;其中模型的持久化,重建和關(guān)聯(lián)交由Repository完成;而單數(shù)據(jù)邏輯(依賴(lài)自身數(shù)據(jù)信息以及關(guān)聯(lián)數(shù)據(jù))則歸于模型(支持泛化);服務(wù)則關(guān)注于任務(wù)處理,包括了多個(gè)模型處理,以及其它服務(wù)的調(diào)用。
如果把一個(gè)系統(tǒng)看作是一個(gè)機(jī)械組件的話(huà),那么領(lǐng)域模型就相當(dāng)于人的骨架;而流程邏輯相當(dāng)于骨架上的肌肉;那么控制邏輯就相當(dāng)于肌肉中的神經(jīng)。
領(lǐng)域模型
概念上,一個(gè)領(lǐng)域模型和普通的符合面向?qū)ο笤瓌t的對(duì)象有聲明區(qū)別:領(lǐng)域模型是業(yè)務(wù)意義上,承載了業(yè)務(wù)數(shù)據(jù)(可以認(rèn)為所有領(lǐng)域模型是有狀態(tài)對(duì)象),從本質(zhì)上說(shuō)它直接來(lái)源于現(xiàn)實(shí)世界,沒(méi)有技術(shù)層次上的考慮,“符合面向?qū)ο笤瓌t的對(duì)象”是用面向?qū)ο蠓椒ǚ治龅玫降模腔谟?jì)算機(jī)領(lǐng)域技術(shù)的(這樣的對(duì)象可以是無(wú)狀態(tài)的);但反過(guò)來(lái),符合面向?qū)ο蟮膶?duì)象不一定反映業(yè)務(wù)領(lǐng)域的模型。
技術(shù)上,領(lǐng)域模型是指那些包含需要被透明持久化的屬性,以及相關(guān)業(yè)務(wù)邏輯的POJO。一個(gè)領(lǐng)域模型包含了這些需要被持久化的業(yè)務(wù)數(shù)據(jù),同時(shí)還包含了與之相關(guān)所有業(yè)務(wù)操作(即能操作所有屬于本模型生命周期內(nèi)的模型數(shù)據(jù)),并且有自己的繼承體系。Martin Fowler認(rèn)為有了這些就可以稱(chēng)為是一個(gè)領(lǐng)域模型,因此在其PoEAA中的ORM包含了一些不透明的持久化方案。我認(rèn)為一個(gè)真正的領(lǐng)域模型需要一個(gè)透明持久化。
注: 領(lǐng)域模型在不同視圖下導(dǎo)致不同的內(nèi)容。比如一個(gè)代理人Agent對(duì)象,在Party的視圖下只擁有基本屬性,而在Sale channel視圖下就保存了一些額外信息如:考核記錄,優(yōu)秀率等。
領(lǐng)域服務(wù)
領(lǐng)域服務(wù)包含的商業(yè)邏輯包含了兩部分:流程邏輯。業(yè)務(wù)領(lǐng)域的流程邏輯(即業(yè)務(wù)流程)。指一系列的業(yè)務(wù)行為,包括維護(hù)一個(gè)或者多個(gè)領(lǐng)域模型。領(lǐng)域服務(wù)是一個(gè)Unit Of Work模式。
領(lǐng)域模型中邏輯僅包括自身生命周期關(guān)聯(lián)數(shù)據(jù)的操作,相當(dāng)于原子操作;只有領(lǐng)域服務(wù)則代表完整的服務(wù)操作邊界,例如一個(gè)領(lǐng)域服務(wù)會(huì)包含對(duì)一個(gè)領(lǐng)域模型的三個(gè)調(diào)用;這一邊界通常也是事務(wù)的控制邊界;
Rod Johnson在《J2EE without EJB》第10章《持久化》里面比較清楚的論述了這個(gè)問(wèn)題:
Workflow methods at the business facade level are still responsible for transaction demarcation, for retrieving persistent objects in the first place, and for operations that span multiple objects, but no longer for domain logic that really belongs to the domain model.
這段話(huà)明白無(wú)誤的講清楚了領(lǐng)域模型應(yīng)該包含什么邏輯,不應(yīng)該包含什么邏輯。領(lǐng)域模型包含的邏輯,這里稱(chēng)之為“領(lǐng)域邏輯”,這些領(lǐng)域邏輯應(yīng)該最小化的依賴(lài)于DAO,而我們討論的那些需要持久化操作參與的事務(wù)性邏輯,這里稱(chēng)之為“workflow methods”,這些“workflow methods”應(yīng)該放在“business facade”,而不應(yīng)該放在領(lǐng)域模型里面。
What logic to put into domain classes versus what to put into workflow controllers should be decided case by case. Operations that are reusable across multiple use case s should normally be part of domain classes. Single use case that are hardly reusable are candidates for controller methods; so are operations that span multiple domain objects.
最后Rod Johnson給出來(lái)一個(gè)區(qū)分的“business workflow logic”和“領(lǐng)域邏輯”的準(zhǔn)則:視具體情況而定,緊密結(jié)合領(lǐng)域模型的,可重用度很高的操作可能是領(lǐng)域邏輯,應(yīng)該放在領(lǐng)域模型里面;比較難重用的控制邏輯方法,特別是可跨越多個(gè)領(lǐng)域模型的操作則放在business facade object里面。
業(yè)務(wù)規(guī)則
這里單列出來(lái),很多時(shí)候業(yè)務(wù)規(guī)則是附屬在領(lǐng)域服務(wù)中的,但在一些特定項(xiàng)目中,業(yè)務(wù)規(guī)則會(huì)被單獨(dú)維護(hù)。
1. 產(chǎn)生一些控制信息,限制或者觸發(fā)某些行為的執(zhí)行(A rule is a declarative statement that applies logic or computation to information values);
2. 產(chǎn)生一些狀態(tài)信息,提供給業(yè)務(wù)人員參考操作(A rule results either in the discovery of new information or a decision about taking action.)。
其它技術(shù)問(wèn)題
應(yīng)用職責(zé)角色分層,必然涉及到兩種對(duì)象,一種是用于展示信息的結(jié)構(gòu)——VO(邊界外通過(guò)編碼方式使用的),一種是DAO對(duì)象。這兩種職責(zé)角色對(duì)象,嚴(yán)格的說(shuō)不算是業(yè)務(wù)設(shè)計(jì)需要關(guān)心的。然而卻和系統(tǒng)開(kāi)發(fā)息息相關(guān)。而業(yè)務(wù)設(shè)計(jì)中的變化導(dǎo)致相關(guān)的工作量卻是巨大的。
VO對(duì)象是為了集成而存在的;其意義是:1. 保護(hù)系統(tǒng)的信息邊界,提供一種結(jié)構(gòu)可以使其它系統(tǒng)或者組件通過(guò)編碼方式獲取系統(tǒng)內(nèi)信息的方式;2. 保護(hù)系統(tǒng)的事務(wù)邊界,領(lǐng)域?qū)ο蠹夹g(shù)上攜帶著持久化信息,通過(guò)VO得以屏蔽。常見(jiàn)的VO對(duì)象存在于Web層和Domain層。
因此,VO對(duì)象的存在只是為了集成而存在,其是否存在的取決于兩個(gè)方面1. 集成的設(shè)計(jì)結(jié)構(gòu);2. 框架的兩個(gè)能力——對(duì)象路徑訪問(wèn)能力以及事務(wù)邊界管理。
Domain層VO對(duì)象,通常是用于不同領(lǐng)域組件間的交互,但隨著架構(gòu)的改進(jìn),集成代碼獨(dú)立存在而不再嵌入到組件內(nèi)部,組件的邊界問(wèn)題保護(hù)不復(fù)存在;更進(jìn)一步的是,框架提供自動(dòng)化的接口適配映射能力的增強(qiáng)。因而VO對(duì)象失去存在的意義。
Web層VO對(duì)象,以SWF為例,早在SWF 1.x時(shí)代,框架就提供了豐富的對(duì)象路徑訪問(wèn)能力,但其Web交互是典型的MVC2方式,事務(wù)邊界在view的render前關(guān)閉,因而導(dǎo)致需要特定的VO對(duì)象來(lái)避免持久化信息問(wèn)題;而SWF 2.x時(shí)代,view的render是在事務(wù)邊界內(nèi),VO不再需要。
針對(duì)維護(hù)Dao對(duì)象而產(chǎn)生成本的一種解決方法是:代碼生成。生成的策略分為兩種環(huán)境,開(kāi)發(fā)環(huán)境和生產(chǎn)環(huán)境。開(kāi)發(fā)環(huán)境實(shí)時(shí)動(dòng)態(tài)生成,可以采用動(dòng)態(tài)代理機(jī)制;而生產(chǎn)環(huán)境要求性能,采用預(yù)編譯生成能力。
另外Web層或者UI層,不需要額外的VO對(duì)象的另一個(gè)理由是:通常Web層的獨(dú)立維護(hù)的成本大于其復(fù)用的價(jià)值。在開(kāi)發(fā)中,Web層需要的信息都來(lái)自Domain層,這樣容易出現(xiàn)Web層的VO對(duì)象和Domain對(duì)象結(jié)構(gòu)一致,雖然這樣便于學(xué)習(xí)和簡(jiǎn)化開(kāi)發(fā),但導(dǎo)致維護(hù)成本的提高,每當(dāng)頁(yè)面需要一個(gè)新的屬性就需要改變太多的類(lèi),同時(shí)Domain對(duì)象設(shè)計(jì)容易限制于Web層,而實(shí)踐中Web層的變化更多于Domain層。正確的路徑是很好的維護(hù)Domain層,同時(shí)不維護(hù)Web層。
設(shè)計(jì)開(kāi)發(fā)
領(lǐng)域模型的四種類(lèi)型
領(lǐng)域模型可以分為四種類(lèi)型:
0. 全局常量對(duì)象
1. 長(zhǎng)生命周期業(yè)務(wù)對(duì)象(類(lèi)似保單對(duì)象);
2. 交易過(guò)程的事務(wù)對(duì)象,幾乎沒(méi)有生命周期的;
3. 業(yè)務(wù)請(qǐng)求對(duì)象和業(yè)務(wù)反饋對(duì)象。這類(lèi)對(duì)象以前沒(méi)有識(shí)別的,通常和VO混在一起;但是在IAA中以及電信業(yè)的模型是這類(lèi)對(duì)象是獨(dú)立存在,并被持久化的;業(yè)務(wù)請(qǐng)求對(duì)象建立在增量更新上很有用。當(dāng)然他們也是幾乎沒(méi)有生命周期的。
并非所有的業(yè)務(wù)系統(tǒng)都擁有這四類(lèi)對(duì)象!相當(dāng)一部分的業(yè)務(wù)系統(tǒng),并沒(méi)有顯著的長(zhǎng)生命周期對(duì)象,因而沒(méi)有明確的增量變更操作類(lèi)型及其規(guī)則,業(yè)務(wù)操作是直接更新業(yè)務(wù)對(duì)象,也就沒(méi)有業(yè)務(wù)請(qǐng)求以及業(yè)務(wù)反饋對(duì)象;同時(shí)此類(lèi)業(yè)務(wù)系統(tǒng)的事務(wù)對(duì)象也通常不存在;
這里要額外補(bǔ)充說(shuō)明的是:
對(duì)于業(yè)務(wù)請(qǐng)求,每個(gè)業(yè)務(wù)請(qǐng)求必需記錄下業(yè)務(wù)時(shí)間;對(duì)于業(yè)務(wù)處理,每個(gè)業(yè)務(wù)處理還可能保留一定的人工干預(yù)控制信息,也將同生命周期的輸入數(shù)據(jù)一起記錄;對(duì)于生命周期,每個(gè)生命周期狀態(tài)的變化都可能會(huì)有獨(dú)立的數(shù)據(jù)需要記錄。
領(lǐng)域模型的級(jí)別
不論是那種類(lèi)型對(duì)象,都擁有一個(gè)屬性,對(duì)象等級(jí);對(duì)于保險(xiǎn)系統(tǒng)來(lái)說(shuō),保單對(duì)象,產(chǎn)品對(duì)象以及組織對(duì)象是一級(jí)對(duì)象,而險(xiǎn)種和角色等都是二級(jí)對(duì)象,其生命周期附屬于一級(jí)對(duì)象;這點(diǎn)對(duì)于設(shè)計(jì)Repository以及服務(wù)粒度都有影響。
領(lǐng)域模型的動(dòng)靜之分
在系統(tǒng)運(yùn)行期間,被頻繁建立和更新的稱(chēng)為“動(dòng)態(tài)”,而在較長(zhǎng)的一段時(shí)間內(nèi)保持穩(wěn)定的稱(chēng)為“靜態(tài)”。
通常而言,“動(dòng)態(tài)”的領(lǐng)域模型群通常代表了系統(tǒng)的核心業(yè)務(wù)對(duì)象。而“靜態(tài)”的領(lǐng)域模型則在業(yè)務(wù)上代表了系統(tǒng)的依存關(guān)系。
領(lǐng)域模型的分析過(guò)程
1. 設(shè)計(jì)一個(gè)貧血的領(lǐng)域模型,包含所有需要被持久化的數(shù)據(jù),除了用例顯示要求的和隱式要求的(根據(jù)業(yè)務(wù)分析出需要的輔助數(shù)據(jù),主要是特定的控制信息),以及與其它領(lǐng)域模型的關(guān)聯(lián)關(guān)系;
2. 考慮是否支持多態(tài)
3. 分析潛在的性能問(wèn)題,根據(jù)需要人為斷開(kāi)存在的關(guān)聯(lián)關(guān)系;
4. 列出相關(guān)的邏輯,利用面向?qū)ο笤O(shè)計(jì)原則,決定哪些邏輯屬于該領(lǐng)域,并加入該對(duì)象模型;
5. 分析是否實(shí)現(xiàn)該對(duì)象的依賴(lài)關(guān)系注入。
業(yè)務(wù)請(qǐng)求對(duì)象的虛實(shí)之道
業(yè)務(wù)請(qǐng)求的概念,與HTTP請(qǐng)求是不同的。為避免誤解,特意加上業(yè)務(wù)一詞修飾。所謂虛實(shí)是指是否將業(yè)務(wù)請(qǐng)求概念實(shí)例化。不做實(shí)例化的理由時(shí)處理簡(jiǎn)單;實(shí)例化則有助于處理業(yè)務(wù)事務(wù)控制以及應(yīng)用賬目模式。一個(gè)業(yè)務(wù)操作上的業(yè)務(wù)請(qǐng)求可能包括多個(gè)請(qǐng)求對(duì)象,與核心業(yè)務(wù)對(duì)象對(duì)應(yīng),例如:在線訂單,就包括了購(gòu)買(mǎi)物品及其數(shù)量和折扣,支付協(xié)議和發(fā)貨協(xié)議等。
對(duì)于沒(méi)有實(shí)例化業(yè)務(wù)請(qǐng)求的情況下,在實(shí)際業(yè)務(wù)操作時(shí),對(duì)每一個(gè)HTTP表單的操作都需要一個(gè)物理的事務(wù)來(lái)支持。這樣做的問(wèn)題是,由于沒(méi)有記錄業(yè)務(wù)請(qǐng)求,直接操作業(yè)務(wù)對(duì)象,在做業(yè)務(wù)日志時(shí)只能記錄操作前以及操作后的信息(既“減肥前,減肥后”);同時(shí)跨越多個(gè)物理事務(wù),要支持查詢(xún)到一次業(yè)務(wù)請(qǐng)求所有操作的信息,需要新建一個(gè)日志索引或者類(lèi)似的手段,在業(yè)務(wù)開(kāi)始時(shí)獲取注冊(cè)一個(gè)索引,所有日志操作中引用這個(gè)索引,在業(yè)務(wù)結(jié)束后關(guān)閉該索引。雖然如此,也帶來(lái)的是業(yè)務(wù)上做回退(undo)以及重做(redo)操作的不便。
但是如果實(shí)例化業(yè)務(wù)請(qǐng)求,就很容易處理這兩樣操作。建立一個(gè)業(yè)務(wù)請(qǐng)求,同時(shí)記錄所涉及的請(qǐng)求數(shù)據(jù)。這樣做的好處是:可以很容易的記錄一些額外的信息;同時(shí)可以很容易的支持審批操作(既俗話(huà)說(shuō)的“管帳的不管錢(qián),管錢(qián)的不管帳”)。
另外,業(yè)務(wù)請(qǐng)求對(duì)象附加好處是,由于某個(gè)領(lǐng)域模塊的增量操作通常從一個(gè)根對(duì)象(即一級(jí)對(duì)象)開(kāi)始,所依賴(lài)的過(guò)濾條件可以從業(yè)務(wù)請(qǐng)求中加以識(shí)別并通過(guò)框架提前加載,而領(lǐng)域服務(wù)對(duì)象的方法接受傳遞對(duì)象而不再關(guān)心對(duì)象的加載工作;同時(shí)也可以通過(guò)框架處理基本數(shù)據(jù)復(fù)制工作,這樣程序只關(guān)心關(guān)聯(lián)對(duì)象的操作。
業(yè)務(wù)請(qǐng)求對(duì)象比較適合明確增量變化的業(yè)務(wù)系統(tǒng),通常這樣業(yè)務(wù)系統(tǒng)在處理變更時(shí),規(guī)則和關(guān)聯(lián)處理很多;對(duì)于全量數(shù)據(jù),采用業(yè)務(wù)請(qǐng)求對(duì)象得不償失,最終兩個(gè)對(duì)象設(shè)計(jì)會(huì)趨于一致;對(duì)于不確定性變化系統(tǒng),如果應(yīng)用業(yè)務(wù)請(qǐng)求對(duì)象會(huì)導(dǎo)致捕獲變化數(shù)據(jù)的困難,應(yīng)該考慮采用更好的業(yè)務(wù)組織方式(如SpringSide的設(shè)計(jì)實(shí)現(xiàn),但SS沒(méi)有考慮批改的流程和日志)。
不過(guò)目前大部分的系統(tǒng)都沒(méi)有處理業(yè)務(wù)請(qǐng)求實(shí)例化,不是所有的業(yè)務(wù)操作需要審批,另外實(shí)例化的麻煩是,已經(jīng)處理了一個(gè)日志對(duì)象,再處理一個(gè)業(yè)務(wù)請(qǐng)求對(duì)象總是讓人多少心里有點(diǎn)不爽;
一個(gè)可能的持久化方式是:記錄對(duì)象路徑及其對(duì)應(yīng)的值;通過(guò)自動(dòng)化的系統(tǒng)掃描,獲取對(duì)象路徑結(jié)構(gòu),以Key和Value的形式記錄在數(shù)據(jù)庫(kù)中,可以避免復(fù)雜的持久化處理;
Repository(Dao)的設(shè)計(jì)開(kāi)發(fā)
Repository是一種特殊的Service,不做任務(wù)處理;而是提供模型的持久化,重建和查詢(xún)工作。由于企業(yè)應(yīng)用大都通過(guò)數(shù)據(jù)庫(kù)實(shí)現(xiàn)持久化,因而Repository和傳統(tǒng)的Dao間的集成設(shè)計(jì)就非常重要。
已知的有三種設(shè)計(jì)方式:
方式 |
描述 |
優(yōu)缺點(diǎn) |
1. 兩個(gè)接口兩個(gè)實(shí)現(xiàn)類(lèi) |
Repository和Dao各自獨(dú)立接口,而通過(guò)Repository實(shí)現(xiàn)類(lèi)轉(zhuǎn)發(fā)請(qǐng)求給Dao實(shí)現(xiàn)類(lèi) |
這種方式雖然正統(tǒng),但是維護(hù)成本太高;一次更新最多要改四處地方 |
2.兩個(gè)接口一個(gè)實(shí)現(xiàn)類(lèi) |
Repository和Dao各自獨(dú)立接口,一個(gè)實(shí)現(xiàn)類(lèi)同時(shí)實(shí)現(xiàn)兩個(gè)接口 |
這種方式就大大簡(jiǎn)化維護(hù)成本;一次更新最多只改一個(gè)接口和一個(gè)實(shí)現(xiàn)類(lèi) |
3. 兩個(gè)接口一個(gè)實(shí)現(xiàn)類(lèi) |
與方式2不同是,Dao接口繼承Repository接口,一個(gè)實(shí)現(xiàn)類(lèi)實(shí)現(xiàn)Dao接口 |
這種方式的維護(hù)成本和方式2差不多,但是當(dāng)接口方法在這個(gè)兩個(gè)接口間流動(dòng)時(shí),可以通過(guò)開(kāi)發(fā)工具完成 |
另外Dao實(shí)現(xiàn)類(lèi)也是工作中開(kāi)發(fā)維護(hù)成本較高的一部分,可以通過(guò)代碼生成降低開(kāi)發(fā)成本。已知的是JDBC 4.0規(guī)范和iBatis 3.0的實(shí)踐。
領(lǐng)域服務(wù)的設(shè)計(jì)開(kāi)發(fā)
業(yè)務(wù)服務(wù)是整個(gè)領(lǐng)域設(shè)計(jì)中另一個(gè)重要的元素;業(yè)務(wù)服務(wù)的如何設(shè)計(jì),并無(wú)定論,但有原則和分類(lèi),最重要的是圍繞著業(yè)務(wù)流程設(shè)計(jì),而其基礎(chǔ)建立于底層模型自身業(yè)務(wù)邏輯的原子操作。
按粒度劃分:
1. 原子服務(wù),業(yè)務(wù)服務(wù)的原子操作;在產(chǎn)品化設(shè)計(jì)中,該層次服務(wù)應(yīng)該擁有擴(kuò)展點(diǎn)和參數(shù)化能力;
2. 組合服務(wù),封裝業(yè)務(wù)服務(wù)的組合操作;在產(chǎn)品化設(shè)計(jì)中,擁有參數(shù)化,擴(kuò)展點(diǎn),事件和集成能力;
3. 還有一類(lèi)業(yè)務(wù)服務(wù)設(shè)計(jì)是實(shí)現(xiàn)于工作流,該層次邏輯關(guān)注于系統(tǒng)集成上;在產(chǎn)品化設(shè)計(jì)中,該層次應(yīng)該擁有事件;
實(shí)際上,原子服務(wù)和組合服務(wù)的粒度劃分并不具備可操作性;只不過(guò)加以標(biāo)識(shí)試圖進(jìn)一步的分析,并為產(chǎn)品化設(shè)計(jì)做基礎(chǔ);
按事務(wù)劃分:
1. 事務(wù)服務(wù),事務(wù)服務(wù)和持久化操作有關(guān),提供事務(wù)邊界;通常是聚合服務(wù);
2. 計(jì)算服務(wù),也算是read-only的事務(wù)服務(wù);計(jì)算服務(wù)的粒度不一定;
由于服務(wù)是針對(duì)領(lǐng)域,因而事務(wù)服務(wù)不關(guān)注于工作流的流程狀態(tài),只關(guān)心相關(guān)領(lǐng)域中長(zhǎng)生命周期領(lǐng)域模型的生命周期;而計(jì)算服務(wù)更不關(guān)心流程相關(guān),只驗(yàn)證輸入合法性,做出計(jì)算,返回結(jié)果,完全是無(wú)狀態(tài)的;工作流則關(guān)心的是相關(guān)領(lǐng)域中的request對(duì)象的業(yè)務(wù)狀態(tài),對(duì)于同一業(yè)務(wù)對(duì)象的并發(fā)處理,應(yīng)該通過(guò)業(yè)務(wù)來(lái)控制;
領(lǐng)域服務(wù)的運(yùn)行模式
簡(jiǎn)單的說(shuō),業(yè)務(wù)處理將被細(xì)化成處理控制器和具體處理器。在這級(jí)別,處理對(duì)于請(qǐng)求的響應(yīng)處理已知的有三種模式:
事件模式(Observer Pattern)、職責(zé)鏈模式(Chain of responsibility Pattern)以及數(shù)據(jù)流模式(Pipes and Filters Pattern)。這幾個(gè)模式處理的各自不同的場(chǎng)景。其中數(shù)據(jù)流模式很適合需要處理大量數(shù)據(jù)的情況。
以下純粹是個(gè)人觀點(diǎn), 不代表任何組織或社團(tuán).
現(xiàn)在流行的編程語(yǔ)言如Java和C#, 大多是面向?qū)ο蟮? 程序的各部分是通過(guò)方法調(diào)用連在一起, 其編程范式是命令編程, 即使支持其他范式也包裝得很難理解和使用.
面向?qū)ο髮?shí)際上只適合實(shí)現(xiàn)抽象數(shù)據(jù)類(lèi)型, 讓它去完成除此之外的任務(wù)確實(shí)是勉為其難, 即便能完成也給人不倫不類(lèi)的感覺(jué), 既不像面向?qū)ο缶幊? 也不像它的前任(過(guò)程編程). 這些語(yǔ)言中的對(duì)象與物質(zhì)世界的物質(zhì)(或?qū)ο?很不一致, 它使用方法調(diào)用的方式與其他對(duì)象進(jìn)行相互作用, 而這與物質(zhì)之間的(通過(guò)通信或媒介)相互作用是截然不同的, 因此用面向?qū)ο鬅o(wú)法很確切地模擬現(xiàn)實(shí)世界(面向?qū)ο笏枷氲某踔?, 更不用說(shuō)準(zhǔn)確地為現(xiàn)實(shí)世界建立模型.
這幾年流行的web服務(wù)和SOA雖使程序間交互更方便, 但它從本質(zhì)上說(shuō)還是使用”面向?qū)ο?命令編程+方法調(diào)用”的思路, 在編程方法論上并沒(méi)有實(shí)質(zhì)的進(jìn)步.
用現(xiàn)在流行的語(yǔ)言實(shí)現(xiàn)的完成復(fù)雜功能的程序邏輯不清晰, 原因在于”命令編程+方法調(diào)用”的設(shè)計(jì)機(jī)制. 這使程序很難模塊化, 副作用無(wú)所不在, 因此很難正確實(shí)現(xiàn)復(fù)雜功能.
現(xiàn)實(shí)世界的發(fā)展變化是通過(guò)事物間的相互作用實(shí)現(xiàn)的, 而這種相互作用用計(jì)算機(jī)科學(xué)的語(yǔ)言來(lái)說(shuō)就是并發(fā)(concurrency). 軟件的本質(zhì)是什么? 我覺(jué)得:軟件總是完成某種功能的,歸根到底是對(duì)現(xiàn)實(shí)世界的事物間相互作用進(jìn)行建模. 因此軟件的組成部分間自然就是并發(fā)的關(guān)系, 而不是過(guò)程調(diào)用的關(guān)系. 用通信進(jìn)程來(lái)對(duì)現(xiàn)實(shí)世界的事物間相互作用進(jìn)行建模是比較合理的. 所以進(jìn)程應(yīng)該作為語(yǔ)言的基礎(chǔ)成分, 是軟件的基本組成部分, 而不是只為了提高效率才采用的.
為了使程序能準(zhǔn)確地為現(xiàn)實(shí)世界建立模型, 從而正確性更高, 結(jié)構(gòu)更合理, 模塊化程度更高, 因此在幾種編程思想或語(yǔ)言的基礎(chǔ)上(見(jiàn)references), 我提出一種新的編程方法論: 面向進(jìn)程+函數(shù)編程+進(jìn)程間通信+邏輯編程+約束編程+其他合理的范式(命令編程除外)和一門(mén)編程語(yǔ)言ProcessLog (全稱(chēng)process logic).
ProcessLog只支持一種并發(fā):通信進(jìn)程. 它就是計(jì)算機(jī)科學(xué)家在上世紀(jì)70年代為了克服現(xiàn)在的Java中仍采用的那種并發(fā)方式的缺點(diǎn)而提出的. 它是經(jīng)過(guò)充分研究得到的一種理想并發(fā)方式, 看了并發(fā)理論(concurrency)和進(jìn)程代數(shù)(Process algebra), 就會(huì)明白這種并發(fā)方式可解決實(shí)際中的各種并發(fā)問(wèn)題, 用它足夠了.
這里的進(jìn)程是進(jìn)程代數(shù)的進(jìn)程,不是過(guò)程,也不是Java中的線程. 看看jcsp或Hoare的CSP(http://www.usingcsp.com/)就明白了.推薦網(wǎng)址:
http://www.cs.kent.ac.uk/projects/ofa/jcsp/,
其中有兩個(gè)ppt說(shuō)得很明白:
"Process Oriented Design for Java: Concurrency for All",
"Communicating Processes, Components and Scaleable Systems".
ProcessLog的語(yǔ)法概要如下:
1 運(yùn)算符
(1) ? 輸入; c ? x 從輸入端口c或通道c上接收輸入值放到變量x中
(2) ! 輸出; c ! v 把v的值從輸出端口或通道c上輸出
(3) -> 順序進(jìn)行的事件的先后關(guān)系
(4) | b : s 分支
(5) || 進(jìn)程并行
(6) // 附屬進(jìn)程
(7) and, or, not 邏輯運(yùn)算符
(8) 算術(shù)運(yùn)算符和關(guān)系運(yùn)算符 與Java中相同
2 程序的組成成分
(1) Unit 程序單元
(2) Process 進(jìn)程
(3) Function 函數(shù)
(4) Predicate 謂詞
(5) Channel 通道, 有兩個(gè)端口: in 輸入端口, out 輸出端口
(6) OutPort 輸出端口
(7) InPort 輸入端口
3 數(shù)據(jù)結(jié)構(gòu)
(1) List (函數(shù)編程中的List類(lèi)型, 對(duì)List的操作函數(shù)與函數(shù)編程中相同);
(2) Tuple 元組, 同Clean.
(3) Set 集合
沒(méi)有數(shù)組
4 進(jìn)程的定義
Process p1 (OutPort pt1, InPort pt2 ){
pt2?x -> pt1! compute(x) -> p1
}
5 進(jìn)程間相互作用
(1) 進(jìn)程并行 process1( c1.out, c2.in)|| process2(c1.in, c2.out)
(2) 附屬進(jìn)程 (getE: getElements || getR: getReleasedVersion) // X.(in?method -> getE ! method ? elems -> getR ! em ? rem-> … ->X)
6 函數(shù)
[Function] compute(double x)=
| x<=0: x*x+3
| x>0: compute(x-5)* compute(x-3)
函數(shù)只能以事件的方式在進(jìn)程中使用或在其他函數(shù)中使用, 不能獨(dú)立使用.
7 謂詞
/* 建圖 */
Predicate createGraph(t, graph):-
addNode(t, null, ({},{}), graph1),
getDS(t, graph1.ns, tlist),
addList(tlist, t, graph1, graph).
/* 加節(jié)點(diǎn) */
Predicate addNode(t, null, (ns, es), (ns1, es):-
merge(ns, {t}, ns1).
Predicate addNode(t, upper, (ns, es), (ns1, es1)):-
merge(ns, {t}, ns1),
merge(es, {(upper, t)}, es1).
8 把謂詞轉(zhuǎn)換為函數(shù)
create(t)= graph
where createGraph(t, graph)
謂詞不能獨(dú)立使用也不能在進(jìn)程中直接使用, 要在進(jìn)程中使用需要先轉(zhuǎn)換為函數(shù).
9 程序單元: 包含進(jìn)程和數(shù)據(jù)類(lèi)型
Unit PmethodDAO;
interface
Tuple Method;
Process getLastVersion(OutPort pt1 , InPort pt2);
…
implementation
Method=(String id, String name, String version);
Process getLastVersion(OutPort pt1 , InPort pt2){
…
}
指導(dǎo)原則: 程序是由通過(guò)通道通信的進(jìn)程組成的. 數(shù)據(jù)處理和算法用函數(shù)編程實(shí)現(xiàn), 如果函數(shù)編程不適用于要處理的問(wèn)題, 就使用邏輯編程或約束編程.
ProcessLog語(yǔ)言限制了編程的隨意性, 要求只能用進(jìn)程代數(shù)+函數(shù)編程+邏輯編程的方式編程, 不允許用Java或c#的命令方式編程.
ProcessLog 現(xiàn)在還沒(méi)有在機(jī)器上實(shí)現(xiàn). 我用該語(yǔ)言重寫(xiě)了實(shí)際項(xiàng)目中的一些復(fù)雜代碼(原是Java代碼), 證實(shí)用它寫(xiě)的程序確實(shí)簡(jiǎn)單清晰, 有類(lèi)似數(shù)學(xué)的簡(jiǎn)潔美. "7 謂詞"就是其中一部分代碼.
我的想法是: 應(yīng)先在紙面上規(guī)定它的語(yǔ)法與語(yǔ)義, 再通過(guò)使用它編寫(xiě)一些應(yīng)用程序來(lái)發(fā)現(xiàn)它的不足,再進(jìn)而改進(jìn)它, 再實(shí)踐, 再改進(jìn), 直到它基本完善再在機(jī)器上實(shí)現(xiàn). 另外, 大家不要把語(yǔ)言分為中國(guó)人提出的還是外國(guó)人提出的, 科學(xué)無(wú)國(guó)界, 這里不存在狹隘的愛(ài)國(guó)主義. 我不是那種技術(shù)高手, 但我自信我是一個(gè)能將理論很好地聯(lián)系實(shí)踐的研究者.
希望有識(shí)之士和我一起共同發(fā)展這種編程方法論和這門(mén)語(yǔ)言.
juwenguang2000@yahoo.com.cn
References
1. CSP http://www.usingcsp.com/
2. JCSP http://www.cs.kent.ac.uk/projects/ofa/jcsp/
3. Clean http://clean.cs.ru.nl/
4. Prolog
5. Delphi
注: 轉(zhuǎn)載時(shí)請(qǐng)注明作者.
說(shuō)的東西,相當(dāng)復(fù)雜。