原文地址:http://www.uml.org.cn/zjjs/201108312.asp
摘要
本文将介绍领域驱动设计(Domain Driven Design)的官方参考架构,该架构分成了Interfaces、Applications和Domain三层以及包含各类基础设施的Infrastructure。本文会对架构中一些重要组件和问题进行讨论,给出一些分析结论。
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. Facade是否是必须的?
尽管在架构中对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并非是一种必须的组件。
分享到:
相关推荐
领域驱动设计(Domain-Driven Design,简称DDD)是一种软件开发方法论,旨在帮助开发者处理复杂的业务逻辑。其核心理念是通过创建一个共享的业务语言来促进业务专家与技术人员之间的沟通,从而构建更加贴合业务需求...
- **Applying Domain-Driven Design and Patterns With Examples in C#**:本书标题明确指出了内容核心,即如何将领域驱动设计(Domain-Driven Design, DDD)与各种设计模式应用于C#编程语言中。它旨在通过具体的C#...
领域驱动设计(Domain-Driven Design,简称DDD)作为一种先进的软件开发方法论,正逐渐被广大开发者所接纳。本篇将结合Spring Boot,深入探讨如何在实际项目中应用领域驱动设计。 一、Spring Boot简介 Spring Boot...
领域驱动设计(Domain-Driven Design,简称DDD)是一种软件开发方法,它强调通过与领域专家紧密合作,将复杂的业务逻辑转化为可操作的软件模型。在本文中,我们将深入探讨领域驱动设计的核心概念、实践策略以及如何...
DDD(Domain-Driven Design,领域驱动设计)是一种软件开发方法论,强调通过深入理解和表达业务领域,来驱动软件的设计和实现。在现代企业级应用开发中,DDD已经成为构建复杂系统的重要工具,特别是在微服务架构中,...
领域驱动设计(Domain-Driven Design,简称DDD)是一种软件开发方法,它强调以业务领域为中心进行系统设计,通过构建领域模型来理解和表达复杂的业务逻辑。这种设计方法特别适合处理复杂业务场景,尤其在面对大型...
在软件开发领域,领域驱动设计(Domain-Driven Design,简称DDD)和CQRS(Command Query Responsibility Segregation,命令查询职责分离)是两种重要的设计模式,它们旨在提高复杂系统的可维护性和可扩展性。...
**领域驱动设计(Domain-driven Design,简称DDD)**是一种强调业务领域逻辑与软件开发紧密结合的方法论,旨在通过深入理解业务需求来指导软件设计,从而更好地满足业务目标。DDD的核心理念在于“领域模型”与“统一...
dddsample-core 是一个基于领域驱动设计(Domain-Driven Design,简称DDD)的开源项目,它以前在SourceForge.net上托管,现在有了新的主页。这个项目为学习和理解DDD提供了一个实践的平台,对于开发者来说,是深入...
领域驱动设计(Domain-Driven Design, DDD)是一种软件开发方法,旨在通过紧密合作,将业务专家和开发团队的知识融入到软件设计中。DDD强调以业务领域为中心,用模型来表达复杂的业务规则和流程。主要组件包括:实体...
在软件开发领域,领域驱动设计(Domain-Driven Design,简称DDD)是一种将业务逻辑与技术实现紧密结合的方法论。它强调以业务领域为中心,通过深入理解业务,构建复杂的软件系统。在这个背景下,我们来详细探讨一个...
在领域驱动设计(Domain-Driven Design)中,领域事件是一个核心概念。它是指领域对象为了让其它对象知道自己已经处理完成某个操作时发出的一个通知。领域事件可以让系统中的不同组件之间解耦,实现松散耦合。 手动...
领域驱动设计(Domain-Driven Design,简称DDD)是一种软件开发方法,它强调以业务领域为中心进行软件设计和开发。在Java开发中,使用DDD可以帮助我们构建更加清晰、可维护且易于扩展的系统。本压缩包"基于DDD的Java...
在软件开发领域,领域驱动设计(Domain-Driven Design,简称DDD)是一种强调以业务领域为中心的软件开发方法论。RubyDDD是将这种设计理念应用于Ruby编程语言的具体实践。本文将深入探讨RubyDDD的核心概念、原则以及...
在 Laravel 开发中,领域驱动设计(Domain-Driven Design,简称 DDD)是一种将业务逻辑与技术实现分离的方法论,它强调以核心业务领域为中心进行软件设计。在 Laravel 5.2 版本中,我们可以充分利用其强大的特性来...
3. **领域驱动设计(Domain-Driven Design, DDD)**:ABP提供了实体(Entities)、值对象(Value Objects)、领域事件(Domain Events)、仓储(Repositories)和领域服务(Domain Services)等DDD概念的实现。...
领域驱动设计(Domain-Driven Design,简称DDD)是一种软件开发方法论,它强调以业务领域为中心进行软件设计和开发。DDD的核心思想是通过深入理解业务领域,将复杂的业务逻辑转化为清晰的模型,以此驱动软件架构的...
Eric Evans在其著作《领域驱动设计》中提出了领域驱动设计(Domain-Driven Design,简称DDD)的概念,而Martin Fowler则在此基础上进一步细化了一系列分析模式,这些模式旨在帮助企业构建更高效、更易于维护的软件...