本文將介紹領域驅動設計(Domain Driven Design)的官方參考架構,該架構分紅了Interfaces、Applications和Domain三層以及包含各種基礎設施的Infrastructure。本文會對架構中一些重要組件和問題進行討論,給出一些分析結論。本文原文鏈接:http://blog.csdn.net/bluishglc/article/details/6681253轉載請註明出處!web
1. 架構概述
2. 架構詳解
2.1. Interfaces-接口層
2.1.1. DTO
2.1.2. Assembler
2.1.3. Facade
2.2. Application-應用層
2.3. Domain-領域層
2.4. Infrastructure-基礎設施層
3. 關於架構的一些討論
3.1. 架構並不能保證領域驅動設計的貫徹與執行
3.2. Fa?ade是不是必須的?
數據庫
領域驅動設計(Domain Driven Design)有一個官方的sample工程,名爲DDDSample,官網:http://dddsample.sourceforge.net/,該工程給出了一種實踐領域驅動設計的參考架構,本文將對此該架構進行簡單介紹,並就一些重要問題進行討論。編程
該架構分紅了Interfaces、Applications和Domain三層以及包含各種基礎設施的Infrastructure。下圖簡略描述了它們之間的關係:設計模式
圖1:領域驅動設計風格的架構草圖(來自於DDDSample官網)安全
下圖是詳細架構:網絡
圖2:領域驅動設計參考架構數據結構
做爲參照,下圖展現了傳統TransactionScript風格的架構,能夠看出,二者的差別並非太大(對於Façade來講,它是一種可選設施,若是系統架構中省略Façade,則DTO與領域對象的互換工做可在service中進行),這也從則面說明推行領域驅動設計的關鍵並不在架構上,而在於整個團隊在分析、設計和開發上沒有自始至終地以領域模型爲核心開展工做,以面向對象的思想進行設計和編程。架構
Transaction Script風格的架構具備明顯的「數據」與「操做」分離的特徵,其和領域驅動設計風格的架構在兩個類組件上有質的區別,一個是領域對象,一個是Service。領域驅動設計的架構核心目標是要建立一個富領域模型,其典型特徵是它的領域對象具備豐富的業務方法用以處理業務邏輯,而Transaction Script風格的領域對象則僅僅是數據的載體,沒有業務方法,這種領域也被稱做「貧血的領域對象」(Anemic Domain Objects)。在Service方面,領域驅動設計的架構裏Service是很是「薄「的一層,其並不負責處理業務邏輯,而在TransactionScript風格的架構裏,Service是處理業務邏輯的主要場所,於是每每很是厚重。app
圖3:數據與操做分離的Transaction Script風格的架構框架
領域驅動設計對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.
該層包含與其餘系統進行交互的接口與通訊設施,在多數應用裏,該層可能提供包括Web Services、RMI或Rest等在內的一種或多種通訊接口。該層主要由Façade、DTO和Assembler三類組件構成,三類組件均是典型的J2EE模式,如下是對三類組件的具體介紹:
DTO- DataTransfer Object(數據傳輸對象),也常被稱做VO-Value Object(值對象)。基於面向對象技術設計的領域對象(即一般所說的「實體」)都是細粒度的,將細粒度的領域對象直接傳遞到遠程調用端須要進行屢次網絡通訊,DTO在設計之初的主要考量是以粗粒度的數據結構減小網絡通訊並簡化調用接口。如下羅列了DTO的多項做用:
- 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應用時序圖(基於《Core J2EE Patterns》插圖進行了修改)
值得一提的是,DTO對實現一個獨立封閉的領域模型具備積極的做用,特別是當系統使用了某些具備自動髒數據檢查(automatic dirty checking)機制的ORM框架時,DTO的優點就更加明顯,不然就會存在領域對象在模型層之外被意外修改並自動持久化到數據庫中的風險或者是像Hibernate那樣的框架因未開啓OpenSessionInView (注:開啓OpenSessionInView有反作用,通常認爲OpenSessionInView不是一種好的實踐)而致使Lazy Loading出現問題。
關於DTO具體的設計用意和應用場景可參考以下資源:
1.《Core J2EE™ Patterns: Best Practices and Design Strategies, SecondEdition》
2.《Patterns of Enterprise ApplicationArchitecture》
在引入DTO後,DTO與領域對象之間的相互轉換工做多由Assembler承擔,所以Assembler幾乎老是同DTO一塊兒出現。也有一些系統使用反射機制自動實現DTO與領域對象之間的相互轉換,Appache的Commons BeanUtils就提供了相似的功能。應該說這兩種實現各有利弊,使用Assembler進行對象數據交換更爲安全與可控,而且接受編譯期檢查,可是代碼量明顯偏多。使用反射機制自動進行象數據交換雖然代碼量不多,但倒是很是脆弱的,一旦對象屬性名發生了變化,數據交互就會失敗,而且很難追蹤發現。整體來講,Assembler更爲直白和穩妥。
圖5.Assebler應用類圖(基於《Core J2EE Patterns》插圖進行了修改)
關於Assembler具體的設計用意和應用場景可參考以下資源:
1.《Core J2EE™ Patterns: Best Practices and Design Strategies, SecondEdition》
2.《Patterns of Enterprise ApplicationArchitecture》
做爲一種設計模式同時也是Interfaces層內的一類組件,Façade的用意在於爲遠程客戶端提供粗粒度的調用接口。Façade自己不處理任何的業務邏輯,它的主要工做就是將一個用戶請求委派給一個或多個Service進行處理,同時藉助Assembler將Service傳入或傳出的領域對象轉化爲DTO進行傳輸。如下羅列了Façade的多項做用:
- 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
實踐Façade的過程當中最難把握的問題就是Façade的粒度問題。傳統的Service均以實體爲單位進行組織,而Façade應該具備更粗粒度的組織依據,較爲合適的粒度依據有:一個高度內聚的模塊一個Façade,或者是一個「聚合」(特指領域驅動設計中的聚合)一個Façade.
圖6.Façade應用類圖(基於《Core J2EE Patterns》插圖進行了修改)
圖7.Façade應用時序圖(基於《Core J2EE Patterns》插圖進行了修改)
關於Assembler具體的設計用意和應用場景可參考以下資源:
1.《Core J2EE™ Patterns: Best Practices and Design Strategies, SecondEdition》
2.《Patterns of Enterprise ApplicationArchitecture》
3.《Design Patterns: Elementsof Reusable Object-Oriented Software》
領域驅動設計對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,在領域驅動設計的架構裏,Service的組織粒度和接口設計依據與傳統Transaction Script風格的Service是一致的,可是二者的實現卻有着質的區別。TransactionScript風格的Service是實現業務邏輯的主要場所,所以每每很是厚重。而在領域驅動設計的架構裏,Application是很是「薄」的一層,全部的Service只負責協調並委派業務邏輯給領域對象進行處理,其自己並真正實現業務邏輯,絕大部分的業務邏輯都由領域對象承載和實現了,這是區別系統是Transaction Script架構仍是Domain Model架構的重要標誌。
不論是Transaction Script風格還Domain Model風格,Service都會與多種組件進行交互,這些組件包括:其餘的Service、領域對象和Repository 或 DAO。
圖8. Service應用時序圖(基於《Core J2EE Patterns》插圖進行了修改)
Service的接口是面向用例設計的,是控制事務、安全的適宜場所。若是Façade的某一方法須要調用兩個以上的Service方法,須要注意事務問題。
領域驅動設計對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層是整個系統的核心層,該層維護一個使用面向對象技術實現的領域模型,幾乎所有的業務邏輯會在該層實現。Domain層包含Entity(實體)、ValueObject(值對象)、Domain Event(領域事件)和Repository(倉儲)等多種重要的領域組件。
領域驅動設計對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.
做爲基礎設施層,Infrastructure爲Interfaces、Application和Domain三層提供支撐。全部與具體平臺、框架相關的實現會在Infrastructure中提供,避免三層特別是Domain層摻雜進這些實現,從而「污染」領域模型。Infrastructure中最多見的一類設施是對象持久化的具體實現。
雖然一個合適的架構對於實施領域驅動設計是大有必要的,但只依靠架構是不能保證領域驅動設計的貫徹與執行的。實際上,在這個參考架構上使用Transaction Script的方式進行開法幾乎沒有任何問題,只要開發人員將領域對象變成「貧血」的「數據載體」對待,在service裏實現業務邏輯,那麼該參考架構將成爲純粹的TransactionScript方式。固然反過來看,這也體現了這一架構的靈活性。確保領域驅動設計的貫徹與執行須要整個團隊在分析、設計和開發上沒有自始至終地以領域模型爲核心開展工做,以面向對象的思想進行設計和編程,才能保證明現領域驅動設計。
儘管在架構中對Façade的定義很是清晰,但在實踐中我發現Façade並非一個容易拿捏的東西。主要問題在於其與service之間的有太多的重疊與類似之處。咱們注意到service是接口是面向一個use case的,所以事務也是追加在service這一層上,因而對於façade而言,99%的狀況是,它只是把某個service的某個方法再包裹一下而已,若是把領域對象和DTO的互轉換工做移至service中進行,那麼façade將完全變成空殼,而關鍵的是:若是service的接口設計是面向和user case的,那麼,毫無疑問,service接口的傳入傳出參數也都應該是DTO,而這一點也在《Core J2EE™ Patterns: Best Practices and Design Strategies, SecondEdition》和《Patterns of Enterprise ApplicationArchitecture》兩書的示例代碼中徹底印證了。那麼,從更爲務實角度出發,Façade並不是是一種必須的組件。