領域驅動設計(Domain Driven Design)參考架構詳解

領域驅動設計(Domain Driven Design)參考架構詳解


摘要


本文將介紹領域驅動設計(Domain Driven Design)的官方參考架構,該架構分成了Interfaces、Applications和Domain三層以及包含各類基礎設施的Infrastructure。本文會對架構中一些重要組件和問題進行討論,給出一些分析結論。本文原文連接:http://blog.csdn.net/bluishglc/article/details/6681253 轉載請註明出處!


目錄


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是否是必須的?


1.      架構概述


領域驅動設計(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是處理業務邏輯的主要場所,因而往往非常厚重。



圖3:數據與操作分離的Transaction Script風格的架構


2.      架構詳解



2.1.    Interfaces-接口層


領域驅動設計對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模式,以下是對三類組件的具體介紹:



2.1.1.   DTO



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》


2.1.2.   Assembler



在引入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》


2.1.3.   Facade



作爲一種設計模式同時也是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》


2.2.    Application-應用層


領域驅動設計對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方法,需要注意事務問題。


2.3.    Domain-領域層


領域驅動設計對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(倉儲)等多種重要的領域組件。


2.4.    Infrastructure-基礎設施層



領域驅動設計對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中最常見的一類設施是對象持久化的具體實現。


3.      關於架構的一些討論   



3.1.    架構並不能保證領域驅動設計的貫徹與執行



雖然一個合適的架構對於實施領域驅動設計是大有必要的,但只依靠架構是不能保證領域驅動設計的貫徹與執行的。實際上,在這個參考架構上使用Transaction Script的方式進行開法幾乎沒有任何問題,只要開發人員將領域對象變成“貧血”的“數據載體”對待,在service裏實現業務邏輯,那麼該參考架構將成爲純粹的TransactionScript方式。當然反過來看,這也體現了這一架構的靈活性。確保領域驅動設計的貫徹與執行需要整個團隊在分析、設計和開發上沒有自始至終地以領域模型爲核心開展工作,以面向對象的思想進行設計和編程,才能保證實現領域驅動設計。


3.2.    Façade是否是必須的?



儘管在架構中對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並非是一種必須的組件。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章