第二部分说明---模型驱动设计的构造块
为了保证软件实现得简洁并且与模型保持一致,不管实际情况如何复杂,必须运用建模和设计的最佳实践。
某些设计决策能够使模型和程序紧密结合在一起,互相促进对方的效用。这种结合要求我们注意每个元素的细节。对细节问题的精雕细琢能够打造出一个稳定的平台,开发人员可以在这个平台上运用第三部分和第四部分中要讲到的建模方法。
当项目遇到或大或小的困难时,开发人员可能会发现这些原则都无法适用于项目当前的状况。为了使领域驱动设计过程更灵活,开发人员需要理解上面这些众所周知的基本原理是如何支 持MODEL-DRIVEN DESIGN的,这样才能在设计过程中做出一些折中选择,而又不脱离正确的轨道。
导航图
描述的是本部分所要讲解的模式以及这些模式彼此关联的方式。共用这些标准模式可以使设计有序进行,也使项目组成员能够更方便地了解彼此的工作内容。同时,使用标准模式也使UBIQUITOUS LANGUAGE更加丰富,所有的项目组成员都可以使用UBIQUITOUS LANGUAGE来讨论模型和设计决策。
开发一个好的领域模型是一门艺术。而模型中各个元素的实际设计和实现则相对系统化。将领域设计与软件系统中的其他关注点分离会使设计与模型之间的关系非常清晰。根据不同的特征来定义模型元素则会使元素的意义更加鲜明。对每个元素使用已验证的模式有助于创建出更易于实现的模型。
分离领域
一、模式:LAYERED ARCHITECTURE
软件中,虽然专门用于解决领域问题的那部分通常只占整个软件系统的很小一部分,但其却出乎意料的重要。要想实现本书的想法,我们需要着眼于模型中的元素并且将它们视为一个系统。
我们需要将领域对象与系统中的其他功能分离,这样就能够避免将领域概念和其他只与软件技术相关的概念搞混了,也不会在纷繁芜杂的系统中完全迷失了领域。
分离领域的复杂技术早已出现,而且都是我们耳熟能详的,但是它对于能否成功运用领域建模原则起着非常关键的作用,所以我们要从领域驱动的视角对它进行简要的回顾。
在面向对象的程序中,常常会在业务对象中直接写入用户界面、数据库访问等支持代码。而一些业务逻辑则会被嵌入到用户界面组件和数据库脚本中。这么做是为了以最简单的方式在短期内完成开发工作。
如果与领域有关的代码分散在大量的其他代码之中,那么查看和分析领域代码就会变得异常困难。对用户界面的简单修改实际上很可能会改变业务逻辑,而要想调整业务规则也很可能需要对用户界面代码、数据库操作代码或者其他的程序元素进行仔细的筛查。这样就不太可能实现一致的、模型驱动的对象了,同时也会给自动化测试带来困难。考虑到程序中各个活动所涉及的大量逻辑和技术,程序本身必须简单明了,否则就会让人无法理解。
软件系统有各种各样的划分方式,但是根据软件行业的经验和惯例,普遍采用LAYERED ARCHITECTURE(分层架构),特别是有几个层基本上已成了标准层。分层这种隐喻被广泛采用,大多数开发人员都对其有着直观的认识。
分层的价值在于每一层都只代表程序中的某一特定方面。这种限制使每个方面的设计都更具内聚性,更容易解释。
尽管LAYERED ARCHITECTURE的种类繁多,但是大多数成功的架构使用的都是下面这4个概念层的某种变体。- 有些项目没有明显划分出用户界面层和应用层,而有些项目则有多个基础设施层。但是将领域层分离出来才是实现MODEL-DRIVEN DESIGN的关键。
小结:
- 给复杂的应用程序划分层次。在每一层内分别进行设计,使其具有内聚性并且只依赖于它的下层。采用标准的架构模式,只与上层进行松散的耦合。将所有与领域模型相关的代码放在一个层中,并把它与用户界面层、应用层以及基础设施层的代码分开。领域对象应该将
重点放在如何表达领域模型上,而不需要考虑自己的显示和存储问题,也无需管理应用任务等内容。这使得模型的含义足够丰富,结构足够清晰,可以捕捉到基本的业务知识,并有效地使用这些知识。
将领域层与基础设施层以及用户界面层分离,可以使每层的设计更加清晰。彼此独立的层更容易维护,因为它们往往以不同的速度发展并且满足不同的需求。层与层的分离也有助于在分布式系统中部署程序,不同的层可以灵活地放在不同服务器或者客户端中,这样可以减少通信开销,并优化程序性能.
一、 1. 将各层关联起来
到目前为止,我们的讨论主要集中在层次划分以及如何分层才能改进程序各个方面的设计上,特别是集中在领域层上。但是显然,各层之间也需要互相连接。在连接各层的同时不影响分离带来的好处,这是很多模式的目的所在。
各层之间是松散连接的,层与层的依赖关系只能是单向的。上层可以直接使用或操作下层元素,方法是通过调用下层元素的公共接口,保持对下层元素的引用(至少是暂时的),以及采用常规的交互手段。而如果下层元素需要与上层元素进行通信(不只是回应直接查询),则需要采用另一种通信机制,使用架构模式来连接上下层,如回调模式或OBSERVERS模式(观察者模式).
最早将用户界面层与应用层和领域层相连的模式是MODEL-VIEW-CONTROLLER(MVC,模型—视图—控制器)框架。它是为Smalltalk语言发明的一种设计模式随后出现的许多用户界面架构都是受到它的启发而产生的。
还有许多其他连接用户界面层和应用层的方式。对我们而言,只要连接方式能够维持领域层的独立性,保证在设计领域对象时不需要同时考虑可能与其交互的用户界面,那么这些连接方式就都是可用的。
通常,基础设施层不会发起领域层中的操作,它处于领域层“之下”,不包含其所服务的领域中的知识。事实上这种技术能力最常以SERVICE的形式提供。
应用层和领域层可以调用基础设施层所提供的SERVICE。如果SERVICE的范围选择合理,接口设计完善,那么通过把详细行为封装到服务接口中,调用程序就可以保持与SERVICE的松散连接,并且自身也会很简单。
然而,并不是所有的基础设施都是以可供上层调用的SERVICE的形式出现的。有些技术组件被设计成直接支持其他层的基本功能(如为所有的领域对象提供抽象基类),并且提供关联机制(如MVC及类似框架的实现)。这种“架构框架”对于程序其他部分的设计有着更大的影响。
一、 2. 架构框架
如果基础设施通过接口调用SERVICE的形式来实现,那么如何分层以及如何保持层与层之间的松散连接就是相当显而易见的。
但是有些技术问题要求更具侵入性的基础设施。整合了大量基础设施需求的框架通常会要求其他层以某种特定的方式实现,如以框架类的子类形式或者带有结构化的方法签名。
最好的架构框架既能解决复杂技术问题,也能让领域开发人员集中精力去表达模型,而不考虑其他问题。然而使用框架很容易为项目制造障碍:要么是设定了太多的假设,减小了领域设计的可选范围;要么是需要实现太多的东西,影响开发进度。
项目中一般都需要某种形式的架构框架。当使用框架时,项目团队应该明确其使用目的:建立一种可以表达领域模型的实现并且用它来解决重要问题。项目团队必须想方设法让框架满足这些需求,即使这意味着抛弃框架中的一些功能。
例如,早期的J2EE应用程序通常都会将所有的领域对象实现为“实体bean”。这种实现方式不但影响程序性能,还会减慢开发速度。现在,取而代之的最佳实践是利用J2EE框架来实现大粒度对象,而用普通Java对象来实现大部分的业务逻辑。不妄求万全之策,只要有选择性地运用框架来解决难点问题,就可以避开框架的很多不足之处。明智而审慎地选择框架中最具价值的功能能够减少程序实现和框架之间的耦合,使随后的设计决策更加灵活。更重要的是,现在许多框架的用法都极其复杂,这种简化方式有助于保持业务对象的可读性,使其更富有表达力。
架构框架和其他工具都在不断的发展。新框架将越来越多的应用技术问题变得自动化,或者为其提供了预先设定好的解决方案。如果框架使用得当,那么程序开发人员将可以更加专注于核心业务问题的建模工作,这会大大提高开发效率和程序质量。但与此同时,我们必须要保持克制,不要总是想着要寻找框架,因为精细的框架也可能会束缚住程序开发人员。
二、领域层是模型的精髓
现在,大部分软件系统都采用了LAYERED ARCHITECTURE,只是采用的分层方案存在不同而已。许多类型的开发工作都能从分层中受益。然而,领域驱动设计只需要一个特定的层存在即可。
领域模型是一系列概念的集合。“领域层”则是领域模型以及所有与其直接相关的设计元素的表现,它由业务逻辑的设计和实现组成。在MODEL-DRIVEN DESIGN中,领域层的软件构造反映出了模型概念。
如果领域逻辑与程序中的其他关注点混在一起,就不可能实现这种一致性。将领域实现独立出来是领域驱动设计的前提。
所有地方,包括本书其他地方,都在倡导的原则)说应该是领域
和UI彼此独立。
采用MODEL-DRIVEN DESIGN的项目团队从项目初始就应该采用模型驱动的设计。当然,即使是经验丰富的项目团队在开发大型软件系统时,也不得不从简单的功能着手,然后在整个开发过程中使用连续的迭代开发。但是最初试探性的工作也应该是由模型驱动的,而且要分离出独立的领域层,否则很有可能项目进行到最后就变成智能用户界面模式了。
如果一个架构能够把那些与领域相关的代码隔离出来,得到一个内聚的领域设计,同时又使领域与系统其他部分保持松散耦合,那么这种架构也许可以支持领域驱动设计。其他的开发风格也有各自的用武之地,但是必须要考虑到各种对于复杂度和灵活性的限制。
在某些条件下,将领域设计与其他部分混在一起会产生灾难性的后果。如果你要开发复杂应用软件并且决定使用MODEL-DRIVEN DESIGN,那么做好准备,咬紧牙关,雇用必不可少的专家,并且
不要使用SMART UI。
四、其他分离方式
遗憾的是,除了基础设施和用户界面之外,还有一些其他的因素也会破坏你精心设计的领域模型。你必须要考虑那些没有完全集成到模型中的领域元素。你不得不与同一领域中使用不同模型的其他开发团队合作。还有其他的因素会让你的模型结构不再清晰,并且影响模型的使用效率。