|
1. 架構(gòu)概述
領(lǐng)域驅(qū)動設(shè)計(jì)(Domain Driven Design)有一個官方的sample工程,名為DDDSample,官網(wǎng):http://dddsample.sourceforge.NET/,該工程給出了一種實(shí)踐領(lǐng)域驅(qū)動設(shè)計(jì)的參考架構(gòu),本文將對此該架構(gòu)進(jìn)行簡單介紹,并就一些重要問題進(jìn)行討論。
該架構(gòu)分成了Interfaces、Applications和Domain三層以及包含各類基礎(chǔ)設(shè)施的Infrastructure。下圖簡略描述了它們之間的關(guān)系:
圖1 領(lǐng)域驅(qū)動設(shè)計(jì)風(fēng)格的架構(gòu)草圖(來自于DDDSample官網(wǎng))
下圖是詳細(xì)架構(gòu):
圖2 領(lǐng)域驅(qū)動設(shè)計(jì)參考架構(gòu)
作為參照,下圖展示了傳統(tǒng)TransactionScript風(fēng)格的架構(gòu),可以看出,兩者的差異并不是太大(對于Fa?ade來說,它是一種可選設(shè)施,如果系統(tǒng)架構(gòu)中省略Fa?ade,則DTO與領(lǐng)域?qū)ο蟮幕Q工作可在service中進(jìn)行),這也從則面說明推行領(lǐng)域驅(qū)動設(shè)計(jì)的關(guān)鍵并不在架構(gòu)上,而在于整個團(tuán)隊(duì)在分析、設(shè)計(jì)和開發(fā)上沒有自始至終地以領(lǐng)域模型為核心開展工作,以面向?qū)ο蟮乃枷脒M(jìn)行設(shè)計(jì)和編程。
Transaction Script風(fēng)格的架構(gòu)具有明顯的“數(shù)據(jù)”與“操作”分離的特征,其和領(lǐng)域驅(qū)動設(shè)計(jì)風(fēng)格的架構(gòu)在兩個類組件上有質(zhì)的區(qū)別,一個是領(lǐng)域?qū)ο螅粋€是Service。領(lǐng)域驅(qū)動設(shè)計(jì)的架構(gòu)核心目標(biāo)是要創(chuàng)建一個富領(lǐng)域模型,其典型特征是它的領(lǐng)域?qū)ο缶哂胸S富的業(yè)務(wù)方法用以處理業(yè)務(wù)邏輯,而Transaction Script風(fēng)格的領(lǐng)域?qū)ο髣t僅僅是數(shù)據(jù)的載體,沒有業(yè)務(wù)方法,這種領(lǐng)域也被稱作“貧血的領(lǐng)域?qū)ο?rdquo;(Anemic Domain Objects)。在Service方面,領(lǐng)域驅(qū)動設(shè)計(jì)的架構(gòu)里Service是非常“薄“的一層,其并不負(fù)責(zé)處理業(yè)務(wù)邏輯,而在TransactionScript風(fēng)格的架構(gòu)里,Service是處理業(yè)務(wù)邏輯的主要場所,因而往往非常厚重。
圖3. 數(shù)據(jù)與操作分離的Transaction Script風(fēng)格的架構(gòu)
2. 架構(gòu)詳解
2. 1 Interfaces-接口層
領(lǐng)域驅(qū)動設(shè)計(jì)對Interfaces的定位是:
Thislayer holds everything that interacts with other systems, such as web services,RMI interfaces or web applications, and batch processing frontends. It handlesinterpretation, validation and translation of incoming data. It also handlesserialization of outgoing data, such as HTML or XML across HTTP to web browsersor web service clients, or DTO classes and distributed facade interfaces forremote Java clients.
該層包含與其他系統(tǒng)進(jìn)行交互的接口與通信設(shè)施,在多數(shù)應(yīng)用里,該層可能提供包括Web Services、RMI或Rest等在內(nèi)的一種或多種通信接口。該層主要由Facade、DTO和Assembler三類組件構(gòu)成,三類組件均是典型的J2EE模式,以下是對三類組件的具體介紹:
2.1.1 DTO
DTO- DataTransfer Object(數(shù)據(jù)傳輸對象),也常被稱作VO-Value Object(值對象)。基于面向?qū)ο蠹夹g(shù)設(shè)計(jì)的領(lǐng)域?qū)ο螅赐ǔKf的“實(shí)體”)都是細(xì)粒度的,將細(xì)粒度的領(lǐng)域?qū)ο笾苯觽鬟f到遠(yuǎn)程調(diào)用端需要進(jìn)行多次網(wǎng)絡(luò)通信,DTO在設(shè)計(jì)之初的主要考量是以粗粒度的數(shù)據(jù)結(jié)構(gòu)減少網(wǎng)絡(luò)通信并簡化調(diào)用接口。以下羅列了DTO的多項(xiàng)作用:
- Reduces NETwork traffic
- Simplifies remote object and remote interface
- Transfers more data in fewer remote calls
- Reduces code duplication
- Introduces stale transfer objects
- Increases complexity due to synchronization and version control
圖4. DTO應(yīng)用時序圖(基于《Core J2EE Patterns》插圖進(jìn)行了修改)
值得一提的是,DTO對實(shí)現(xiàn)一個獨(dú)立封閉的領(lǐng)域模型具有積極的作用,特別是當(dāng)系統(tǒng)使用了某些具有自動臟數(shù)據(jù)檢查(automatic dirty checking)機(jī)制的ORM框架時,DTO的優(yōu)勢就更加明顯,否則就會存在領(lǐng)域?qū)ο笤谀P蛯右酝獗灰馔庑薷牟⒆詣映志没綌?shù)據(jù)庫中的風(fēng)險(xiǎn)或者是像Hibernate那樣的框架因未開啟OpenSessionInView (注:開啟OpenSessionInView有副作用,一般認(rèn)為OpenSessionInView不是一種好的實(shí)踐)而導(dǎo)致Lazy Loading出現(xiàn)問題。
關(guān)于DTO具體的設(shè)計(jì)用意和應(yīng)用場景可參考如下資源:
- 《Core J2EE? Patterns: Best Practices and Design Strategies, SecondEdition》
- 《Patterns of Enterprise ApplicationArchitecture》
2.1.2 Assembler
在引入DTO后,DTO與領(lǐng)域?qū)ο笾g的相互轉(zhuǎn)換工作多由Assembler承擔(dān),因此Assembler幾乎總是同DTO一起出現(xiàn)。也有一些系統(tǒng)使用反射機(jī)制自動實(shí)現(xiàn)DTO與領(lǐng)域?qū)ο笾g的相互轉(zhuǎn)換,Appache的Commons BeanUtils就提供了類似的功能。應(yīng)該說這兩種實(shí)現(xiàn)各有利弊,使用Assembler進(jìn)行對象數(shù)據(jù)交換更為安全與可控,并且接受編譯期檢查,但是代碼量明顯偏多。使用反射機(jī)制自動進(jìn)行象數(shù)據(jù)交換雖然代碼量很少,但卻是非常脆弱的,一旦對象屬性名發(fā)生了變化,數(shù)據(jù)交互就會失敗,并且很難追蹤發(fā)現(xiàn)。總體來說,Assembler更為直白和穩(wěn)妥。
圖5. Assebler應(yīng)用類圖(基于《Core J2EE Patterns》插圖進(jìn)行了修改)
關(guān)于Assembler具體的設(shè)計(jì)用意和應(yīng)用場景可參考如下資源:
- 《Core J2EE? Patterns: Best Practices and Design Strategies, SecondEdition》
- 《Patterns of Enterprise ApplicationArchitecture》
2.1.3 Facade
作為一種設(shè)計(jì)模式同時也是Interfaces層內(nèi)的一類組件,F(xiàn)acade的用意在于為遠(yuǎn)程客戶端提供粗粒度的調(diào)用接口。Facade本身不處理任何的業(yè)務(wù)邏輯,它的主要工作就是將一個用戶請求委派給一個或多個Service進(jìn)行處理,同時借助Assembler將Service傳入或傳出的領(lǐng)域?qū)ο筠D(zhuǎn)化為DTO進(jìn)行傳輸。以下羅列了Facade的多項(xiàng)作用:
- Introduces a layer that provides services to remote clients
- Exposes a uniform coarse-grained interface
- Reduces coupling between the tiers
- Promotes layering, increases flexibility and maintainability
- Reduces complexity
- Improves performance, reduces fine-grained remote methods
- Centralizes security management
- Centralizes transaction control
- Exposes fewer remote interfaces to clients
實(shí)踐Facade的過程中最難把握的問題就是Facade的粒度問題。傳統(tǒng)的Service均以實(shí)體為單位進(jìn)行組織,而Facade應(yīng)該具有更粗粒度的組織依據(jù),較為合適的粒度依據(jù)有:一個高度內(nèi)聚的模塊一個Facade,或者是一個“聚合”(特指領(lǐng)域驅(qū)動設(shè)計(jì)中的聚合)一個Facade.
圖6. Facade應(yīng)用類圖(基于《Core J2EE Patterns》插圖進(jìn)行了修改)
圖7. Facade應(yīng)用時序圖(基于《Core J2EE Patterns》插圖進(jìn)行了修改)
關(guān)于Assembler具體的設(shè)計(jì)用意和應(yīng)用場景可參考如下資源:
- 《Core J2EE? Patterns: Best Practices and Design Strategies, SecondEdition》
- 《Patterns of Enterprise ApplicationArchitecture》
- 《Design Patterns: Elementsof Reusable Object-Oriented Software》
2.2 Application-應(yīng)用層
領(lǐng)域驅(qū)動設(shè)計(jì)對Application的定位是:
Theapplication layer is responsible for driving the workflow of the application,matching the use cases at hand. These operations are interface-independent andcan be both synchronous or message-driven. This layer is well suited forspanning transactions, high-level logging and security. The application layeris thin in terms of domain logic - it merely coordinates the domain layerobjects to perform the actual work.
Application層中主要組件就是Service,在領(lǐng)域驅(qū)動設(shè)計(jì)的架構(gòu)里,Service的組織粒度和接口設(shè)計(jì)依據(jù)與傳統(tǒng)Transaction Script風(fēng)格的Service是一致的,但是兩者的實(shí)現(xiàn)卻有著質(zhì)的區(qū)別。TransactionScript風(fēng)格的Service是實(shí)現(xiàn)業(yè)務(wù)邏輯的主要場所,因此往往非常厚重。而在領(lǐng)域驅(qū)動設(shè)計(jì)的架構(gòu)里,Application是非常“薄”的一層,所有的Service只負(fù)責(zé)協(xié)調(diào)并委派業(yè)務(wù)邏輯給領(lǐng)域?qū)ο筮M(jìn)行處理,其本身并真正實(shí)現(xiàn)業(yè)務(wù)邏輯,絕大部分的業(yè)務(wù)邏輯都由領(lǐng)域?qū)ο蟪休d和實(shí)現(xiàn)了,這是區(qū)別系統(tǒng)是Transaction Script架構(gòu)還是Domain Model架構(gòu)的重要標(biāo)志。
不管是Transaction Script風(fēng)格還Domain Model風(fēng)格,Service都會與多種組件進(jìn)行交互,這些組件包括:其他的Service、領(lǐng)域?qū)ο蠛蚏epository 或 DAO。
圖8. Service應(yīng)用時序圖(基于《Core J2EE Patterns》插圖進(jìn)行了修改)
Service的接口是面向用例設(shè)計(jì)的,是控制事務(wù)、安全的適宜場所。如果Facade的某一方法需要調(diào)用兩個以上的Service方法,需要注意事務(wù)問題。
2.3 Domain-領(lǐng)域?qū)?/strong>
領(lǐng)域驅(qū)動設(shè)計(jì)對Domain的定位是:
Thedomain layer is the heart of the software, and this is where the interestingstuff happens. There is one package per aggregate, and to each aggregatebelongs entities, value objects, domain events, a repository interface andsometimes factories.
Thecore of the business logic belongs in here. The structure and naming ofaggregates, classes and methods in the domain layer should follow theubiquitous language, and you should be able to explain to a domain expert howthis part of the software works by drawing a few simple diagrams and using theactual class and method names of the source code.
Domain層是整個系統(tǒng)的核心層,該層維護(hù)一個使用面向?qū)ο蠹夹g(shù)實(shí)現(xiàn)的領(lǐng)域模型,幾乎全部的業(yè)務(wù)邏輯會在該層實(shí)現(xiàn)。Domain層包含Entity(實(shí)體)、ValueObject(值對象)、Domain Event(領(lǐng)域事件)和Repository(倉儲)等多種重要的領(lǐng)域組件。
2.4 Infrastructure-基礎(chǔ)設(shè)施層
領(lǐng)域驅(qū)動設(shè)計(jì)對Infrastructure的定位是:
Inaddition to the three vertical layers, there is also the infrastructure. As thethe picture shows, it supports all of the three layers in different ways,facilitating communication between the layers. In simple terms, theinfrastructure consists of everything that exists independently of ourapplication: external libraries, database engine, application server, messagingbackend and so on.
Also,we consider code and configuration files that glues the other layers to theinfrastructure as part of the infrastructure layer. Looking for example at thepersistence ASPect, the database schema definition, Hibernate configuration andmapping files and implementations of the repository interfaces are part of theinfrastructure layer.
Whileit can be tricky to give a solid definition of what kind of code belongs to theinfrastructure layer for any given situation, it should be possible tocompletely stub out the infrastructure in pure Java unit/scenario tests andstill be able to use the domain layer and possibly the application layer towork out the core business problems.
作為基礎(chǔ)設(shè)施層,Infrastructure為Interfaces、Application和Domain三層提供支撐。所有與具體平臺、框架相關(guān)的實(shí)現(xiàn)會在Infrastructure中提供,避免三層特別是Domain層摻雜進(jìn)這些實(shí)現(xiàn),從而“污染”領(lǐng)域模型。Infrastructure中最常見的一類設(shè)施是對象持久化的具體實(shí)現(xiàn)。
3. 關(guān)于架構(gòu)的一些討論
3.1 架構(gòu)并不能保證領(lǐng)域驅(qū)動設(shè)計(jì)的貫徹與執(zhí)行
雖然一個合適的架構(gòu)對于實(shí)施領(lǐng)域驅(qū)動設(shè)計(jì)是大有必要的,但只依靠架構(gòu)是不能保證領(lǐng)域驅(qū)動設(shè)計(jì)的貫徹與執(zhí)行的。實(shí)際上,在這個參考架構(gòu)上使用Transaction Script的方式進(jìn)行開法幾乎沒有任何問題,只要開發(fā)人員將領(lǐng)域?qū)ο笞兂?ldquo;貧血”的“數(shù)據(jù)載體”對待,在service里實(shí)現(xiàn)業(yè)務(wù)邏輯,那么該參考架構(gòu)將成為純粹的TransactionScript方式。當(dāng)然反過來看,這也體現(xiàn)了這一架構(gòu)的靈活性。確保領(lǐng)域驅(qū)動設(shè)計(jì)的貫徹與執(zhí)行需要整個團(tuán)隊(duì)在分析、設(shè)計(jì)和開發(fā)上沒有自始至終地以領(lǐng)域模型為核心開展工作,以面向?qū)ο蟮乃枷脒M(jìn)行設(shè)計(jì)和編程,才能保證實(shí)現(xiàn)領(lǐng)域驅(qū)動設(shè)計(jì)。
3.2 Facade是否是必須的?
盡管在架構(gòu)中對Facade的定義非常清晰,但在實(shí)踐中我發(fā)現(xiàn)Facade并不是一個容易拿捏的東西。主要問題在于其與Service之間的有太多的重疊與相似之處。我們注意到Service是接口是面向一個use case的,因此事務(wù)也是追加在Service這一層上,于是對于Facade而言,99%的情況是,它只是把某個Service的某個方法再包裹一下而已,如果把領(lǐng)域?qū)ο蠛虳TO的互轉(zhuǎn)換工作移至Service中進(jìn)行,那么Facade將徹底變成空殼,而關(guān)鍵的是:如果Service的接口設(shè)計(jì)是面向和user case的,那么,毫無疑問,Service接口的傳入傳出參數(shù)也都應(yīng)該是DTO,而這一點(diǎn)也在《Core J2EE Patterns: Best Practices and Design Strategies, SecondEdition》和《Patterns of Enterprise ApplicationArchitecture》兩書的示例代碼中完全印證了。那么,從更為務(wù)實(shí)角度出發(fā),F(xiàn)acade并非是一種必須的組件。
it知識庫:領(lǐng)域驅(qū)動設(shè)計(jì)(Domain Driven Design)參考架構(gòu)詳解,轉(zhuǎn)載需保留來源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。