UML软件工程组织

 Patterns of Enterprise Application Architecture 企业级应用架构模式 02简介 
 wzb_saxon(翻译)(选自中国软件)

大家好,看到这个标题的同志,一定对企业级应用开发很关心,作为一个还未毕业的本科生翻译这本书的难度,不用说大家也知道,还好在一个具体的项目中我的认知机会还是比较多的,这本书给我打了很好的理论基础,让我长了很大的见识,知道了程序也能使这样编的,本来翻译它是作为项目组中的技术文档,但是这本书的重要性决定了将它独占未免有点贪婪,贴在网上,让所有程序员朋友看看,大家一同提高,翻译中不足之处请指正,对迎面而来的非议,谩骂,我也做好了十足准备,我的路才刚刚开始,希望大家,不吝赐教                 

Introduction   简介

建造一个计算机系统是多么的困难,想必你一定知道。随着系统的复杂性越来越高,软件构造的难度将呈几何指数级增长。同其他各行各业一样,我们只能在失败和成功中学习不断进步,这本书正是将这种思想,用文字的方式表达出来,希望能帮助读者尽快地掌握这些课程,可以和别人进行有效的沟通

关于这篇简介,先指定一下本书的范围,再提供一些支撑本书涉及思想的背景。

Architecture//架构

软件业总喜欢引入一些词语,将他们的意思无限扩展,变得驴头不对马嘴,这其中最大的受害者莫过于架构(architecture)了,我总倾向于把架构看作是一个给人印象深刻又很好听的词,用来表示我们时下正在谈论一件非常非常重要的东西。但我又得非常现实,从而不让我的玩世不恭影响到这本书的迷人魅力

在这本书中,主要精力集中在一个企业应用的核心部分,以及他们是如何被组织起来的。其中的某些模式理所当然的称为架构模式,因为他们代表了有关这些部分的重要决策,其他更多的是帮助你理解这个架构的设计模式

处于支配地位的架构模式---是多层模式,将在第二章中深入阐述。这本书就是关于如何将企业应用分层,以及如何组织各层工作的。绝大多数知名的企业应用中都使用了某些形式的多层架构。但在某些情况下,使用其他方法,例如管道,过滤器或许更为有效。我并不涉及这些情形,而是将重心放在多层架构的内容上,因为它的用途最为广泛。

Enterprise Applications//企业应用

许许多多的人都在写软件,我们都称之为软件开发,软件的种类也是五花八门,每个都有其自己的挑战和复杂性。当我和一些电信领域的朋友探讨时,谈到了这点。在某些方面,企业级应用比电信软件简单得多—没有头疼的多线程问题,也没有软硬件集成的问题。但在其他方面,问题就比较严重了,企业级应用常常要处理海量的复杂的数据,其中还涉及到很多,通用的推理逻辑并不适用的业务规则;虽然的确有某些通用的软件技术和模式,但多数仅适合在一个特定的领域上。

在我的职业生涯中,大部分精力都放在了企业级应用上,所以我的模式全部都是关于企业级应用的。但是你要问我“企业级应用”到底指的是什么呢?我很难给出一个精确的定义,但我能给你一些相关的指点:

让我们从实例开始吧。

所谓的“企业级应用”包括以下这些诸如:薪水,病人记录,物价分析,信用积分,保险,会计业务,外汇兑换贸易。

相对的那些“非企业级应用”包括:汽车输油,字处理,电梯控制器,化工厂控制器,电话开关、操作系统,编译器,游戏

企业级应用经常涉及数据的持久保存。由于数据在程序运行中到处都要用到,因此必须持久保存而且通常要保存很多年。在这段时间里,使用数据的程序也会经常的发生改变。常见的情况是,数据的寿命比创造他们的硬件系统、操作系统、编译器还要长。这段时间里,在不影响原有信息的前提下,为了存储新的信息,数据的结构也会因此发生很多变化。即使发生了一个根本性的变化--公司为了处理一个业务,安装了一个全新的应用系统,这些数据也必须移植到新的应用系统上

通常企业级应用包含的数据量很大,一个中型的系统就会包含超过1GB的数据量—被组织成上千万条记录。管理这些数据就成为这个系统的主要部分。较早的系统使用索引文件结构像ibm’s VSAM & ISAM。现代的系统经常使用数据库,大多数是关系型数据库。数据库的设计和数据的填充已经变成了其自身的一个分支专业了。

通常很多人并发地存取数据。对很多系统来说,使用人员可能不到百人,但是对 internet上基于web的系统来说,使用者的递增速率就是几何级的。这么多的使用者,确保他们都能从系统中正常地访问数据就是一个非常重要的问题。但即使没有那么多用户,如何保证两个人不会在同一时刻对同一个数据进行存取-----这个经常导致出错的问题不会发生。用户数量过多带来的沉重压力,通过事务管理工具来处理仅仅只能解决一部分,而通常应用开发人员也根本不可能掩盖这种问题。

为了应付日益庞大的数据量,大量的UI界面被投入使用,所以即使出现成百上千个截然不同的界面也并不希奇。企业级应用的用户与专业用户的习惯差异很大,他们很少有技术层面的专长。为了满足不同的需求,数据的表现形式也是千差万别的。

企业级应用并不是孤家寡人,他们经常需要和遍布在公司角落的其他企业级应用集成在一起。这些不同的系统通常是在不同的时期,使用不同的技术建成的。即便是合作的机制也不一样---COBOL 数据文件,CORBA,消息系统。企业会尽力将其不同的系统通过一个通用的通信技术集成起来。当然即使这样也很难完满地完成任务。所以企业会同时使用几套不同的统一集成方案。

即使公司统一了集成的技术,他们也经常碰到千差万别的业务处理方式,不统一的数据概念这些问题。公司的一个部门可能会认为:顾客是一个和公司拥有正式协议的人。另一个部门会把那些以前订过合同的那些人也算上,虽然现在已经解除了;还有一个部门会把产品销售算进去,服务销售排除在外。乍听上去可能会感觉这很简单,很好解决,但是当成百上千条记录,在每个领域都有截然不同的意思时,问题的艰巨程度不能不说是一个严峻的挑战,即便公司里有人能够区分不同领域不同的意思。(当然所有的这一切都在暗自变化当中,不会有任何提醒),结果数据不得不被经常读取,munged,按照各种各样的不同的语法,语义格式记录下来。

接下来就是有关“业务逻辑“这个术语了。我发现这个术语非常古怪,因为没有比业务逻辑更没逻辑的东西了。当你在实现一个操作系统时,你竭尽全力保证所有的一切都是符合逻辑的。但是对于强加给你的业务规则,你会束手无策。规则通常都非常的随意,看上去好像没有任何的逻辑。当然他们之所以这样也是有原因的:eg为了照顾客户的账目周期,有些商人会协商年终结账比惯例晚两天,这样做他便赢得了一项将近百万美元的业务。正是这些特殊的情况层出不穷,导致了业务的复杂性,无逻辑性,业务处理软件为什么是那么难以生成。

对某些人来说,术语“企业级应用“隐含着这是个大型系统。但是切记并不是所有的企业级应用都是大型的,虽然这些应用给企业带来了巨大的财富。许多人总认为既然是个小型系统,也就犯不上伤脑筋了。某种程度上,的确是这样的,如果一个小型系统失败了,不会像大型系统那样,招致过多的非议。然而我认为正是这种麻痹地思想,忽视了诸多小型项目所产生的累计效应。如果你能改进企业中所有的小型项目,那么对企业来说,产生的累计效应会相当的显著,这主要是由于小型项目总会产生与其规模不相称的价值。实际上,通过简化一个大型项目的体系架构和操作过程,将其转化成一个小型的项目,也是一件很重要的工作。

Kinds of enterprise application//企业级应用的种类

当我们在讨论:如何设计一个企业级应用,应该采用何种模式的时候,能够意识到不同类型企业级应用间的区别,再采取相应的方法来解决问题,才是最重要的。当人们说到“这个应该这么做,那个应该那么做”的时候,我的警钟就开始嗡嗡作响了。对我来说,绝大多数设计中的挑战(和兴趣)是:对各种不同方法的了然于胸,知道什么时候该采用什么样的方法,而不是采用其他方法。在名目繁多的方法中,我只挑出来三点。

1.考虑一个B2C(business to customer)的在线零售业:人们手推小车,浏览购物。

对于这种系统我们必须能够处理超大容量的用户数量,因此我们的解决方法在资源利用率方面必须既相当的高效,又必须是可升级的—你可以通过添加硬件设备来处理增大的负载,对应于这种的应用的领域逻辑是非常简单的:获取订单,一些相对简单的计费和运送计算,以及发货的通知。为了让任何人都能轻松的面对这个系统,我们必须建造漂亮的通用的web表现形式,可以在任何可能的浏览器中使用。数据源包括一个容纳订单数据的数据库,或许还包含某些通讯---一个存货系统,用来提交货物储备和发送的信息。

2.和一个自动处理租契协议的系统相比,在某些方面,后者比前者要简单的多—用户非常少,在任何时候不会超过百人。然而在业务逻辑上就要复杂的多了。要按月对每份契约的账单进行计算,还要处理诸如提前归还、延期支付、对每份在册的契约数据进行审核等等的事件,所有的这一切都是复杂的任务,由于多数租契业的竞争的白热化,形式的复杂多样,已经今非昔比了。像这样复杂的业务领域是富有挑战性的,因为业务规则是非常随意的。当设计一个操作系统时或是一个电话开关时,你至少是在按照一种逻辑的方法在开发整个处理过程。业务规则违背了一切逻辑,在任何时候发生改变都是不足为奇的。

这样一个系统也常常有着更为复杂的UI.至少这意味着:涉及更多html接口和更复杂的界面。经常这种类型的系统对UI的需求都是很大的,用户会要求一个更复杂的表现,仅仅一个HTML前端所能提供的是远远不够的,所以一个更为传统的胖客户端界面就必不可少了。然而复杂的用户交互也导致了过于复杂的交易习惯:现在在一个一般的交易中,登记一个契约就可能会花费一两个小时的时间。同时,我们也会发现一个复杂的数据库方案:包含了好几百张表,连同资产评估、标价包裹。

3.第三个例子,是一个小公司里简单的费用查询系统。

这样一个系统涉及很少的用户和非常简单的逻辑。通过HTML表现出来,在公司里运行操作也很简单。唯一的数据源就是一个数据库里的几张表。但即便这样一个简单的系统也不乏挑战。你必须迅速构造这个系统。同时你还要切记:当人们需要计算赔偿单并将他们加入薪水册中、了解税费涉及的款项、向CFO提交报告、和一个飞机预定的web 服务连接时…所有的这一切都会让系统臃肿起来。如果你试图使用前面提到的两个系统的架构中的某个,那就会减慢系统的开发进度,系统的复杂度也会增加,对系统日后的升级很可能会产生很大的伤害。这个系统或许是很小,但是大多数企业都有很多这样的系统,所以一个过度复杂架构的累计效应会是非常显著的

Performance//性能

在像这样的一本书里谈论性能通常是很困难的。为什么呢?因为任何一个有关性能的建议都不会被认作为事实,直到在你的配置中将它作为衡量的标准。由于出于对性能的考虑,种种的设计被采用或拒绝,而一旦某人在应用中作出某些衡量的话,这一切都将是形同虚设--这类情况我见的太多了。

我在本书中给出了不少指南,包括将远程调用减少到最低程度---这经常是一个提高性能的好建议。即便这样你也必须在自己的应用中进行权衡来确认采纳这些建议,类似的,为了更为通俗易懂,在本书中的某些场合中提供的实例代码,不得不在性能上作出牺牲。对你的系统进行优化只能由你来决定了。但每当进行性能优化的时候你都必须思前想后,否则你会无意之中将你的代码变得晦涩难懂。

在这点上有个重要的推论:配置中任何明显的改变都可能会导致任何性能相关事实的失效。所以如果你将计算机、硬件设备、数据库、等等所有的一切都升级到一个新的版本时,---重新进行性能优化,并确保他们仍在起作用。在很多情况下,一个全新的配置会让情况有所改观。实际上你或许会发现在以前为了提升性能对应用所做的优化,在新环境中,正损害着性能。

讨论性能时另一个问题是:许多术语正在被歪解的事实。最为著名的牺牲者就是“可测量性“了,到处滥用,什么意思都有。对我这样一个愤世嫉俗的人来说,大多数有关会影响可测量性的东西的评论都是说不清的威胁 对性能说不清楚的日后的担心。当然正是因为他们是日后的威胁,通过衡量来确认他们也是非常困难的。下面是我使用的术语:

响应时间(response time)--系统处理从外界来的一个请求所耗费的时间。这可能是一个ui操作,像单击一个按钮,或是对server 的api调用。

相对于处理来说,响应灵敏度(responsiveness)是指系统接受请求有多快。对多数系统来说这点是非常重要的,因为面对一个响应灵敏度极低的系统时,人们都会非常丧气,即使它的响应时间很好。如果你的系统在整个请求过程中都处于等待状态,那么你的响应灵敏度和响应时间是一样的。然而如果你在完成之前就能指出你已经接收到请求,那么你的响应灵敏度就很好。在拷贝文件时提供一个进度条会改善你的ui响应灵敏度,即使它不会提高响应时间。

延迟(latency)是得到任何形式系统的响应所需的最短时间,即使待处理的工作事实上并不存在。延迟通常是远程系统中的大问题。如果我发出一个指令要求一个程序什么也不做,但却告诉我它已经完成了(什么也没做),如果这个程序是在我的笔记本上的话,我会马上获得一个即时响应。然而如果程序运行在一个远方电脑上时,我或许得等上好几秒钟的时间,请求和响应在线路上传输所花费的时间。作为一个应用的开发人员,通常对延迟是无可奈何的。也正是如此,你必须将你的远程调用降低到最小程度。

吞吐量(throughput) 你在给定的时间里完成的任务量。当衡量一个文件拷贝时,吞吐量可能是按字节数每秒 来衡量的。对企业级应用,典型的衡量手段是 每秒处理的交易量,但是相关的问题是这取决于你的交易的复杂程度。对你特定的系统,你应该挑选出一个常用的交易集。系统的负载是指你的系统承受多大的压力。或许能用系统在线用户的数量来衡量。

负载(load)的度量—响应时间提供了上下文。所以你可能会说,某个请求的响应时间 10个人时是0.5秒,20个人时是2秒。负载灵敏度(load sensitivity)是描述响应时间如何随负载变化的。一个拥有 10人到20人时0.5秒响应时间的系统 其负载灵敏度 比一个响应时间是 10人时0.2秒,20人时上升到2秒的 系统 要低。一个系统的容量(capacity) 指出了系统最大有效吞吐量和负载,这可能是一个绝对值,或者是响应时间所能忍受的最低点

可升级性(scalability) 是衡量:添加硬件设备对性能的影响的。一个可升级的系统,允许你通过添加硬件设备已获得相应性能的提升,像通过加倍服务器数量来成倍提高吞吐量。

问题是设计时的决策,对所有这些性能因素的影响并不是同等的。例如在一台服务器上运行着两个软件系统:旗鱼有20tps的容量,骆驼有40tps的容量。那个性能更好呢,哪个升级性更好呢?但从这个数据上我们并不能回答可升级性的问题,我们只能说在单一服务器上骆驼拥有更高的容量。

现在如果我们再加一台服务器,我们发现旗鱼能处理35tps,骆驼能处理50tps了。骆驼的容量还是更高,但旗鱼好像升级地更明显。接下来,我们继续添加服务器,发现旗鱼每次递增15tps,骆驼每次递增10tps。通过这个数据我们会说,在不足5台服务器的情况下,旗鱼升级地更快,虽然骆驼有着明显的容量优势。

当建造企业级系统时,考虑硬件可升级性而不是容量或者负载的灵敏度是很有意义的。当你需要更好的性能时,可升级性会给你选择的机会 ,而且可升级性通常都很容易实现。通常设计者,会在一个特定的硬件平台上,做一些复杂的事情来提高容量,而通过添置更多的硬件可能会更便宜。如果骆驼比旗鱼的花费要高很多的话,多出来的花费正好和几个服务器相当,可是如果你只需要旗鱼具有40 tps的容量的话,这些服务器也没发挥什么大用了。不得不依靠更好地设备,来让我们的软件更好的运行----这种抱怨很时髦,当我不得不更新我的笔记本来跟上最新版时,我也加入到这一阵营中来。别忘了新出的硬件设备,总是比让软件在一个不那么强大的系统上运行,支出的花费要少得多,类似的,如果便宜的话添置更多的服务器,再增加更多的程序员---会提供一个可升级的系统。

Patterns//模式

模式已经存在很长的时间了,我也不愿再重溯他们的历史。我将借此机会,将我对模式的认识告诉大家,以及为什么说通过使用模式这一途径来描述设计是非常值得的,有意义的。

现在并没有一个被广泛认可的关于模式的定义,但或许我们可以从Christopher Alexander谈起,从他给每一位模式狂热者带来灵感的名言开始:"Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice" [Alexander et al, pattern language].Alexander 是一位建筑师,他所谈的也都是关于建筑物的,但是这个定义对软件来说也相当适用。模式的焦点是一个特定的解决方法。一个既通用又高效的方法,能够处理一个或更多个重复出现的问题。看待它的另一种方式是:它是相当数量的建议。创造模式的艺术,就是将很多条建议分类成相关的独立的框框,你可以参考他们,可以或多或少的将他们单独提出来讨论讨论。

模式的一个关键部分是:他们植根于实践中。你是通过观察人们的行为,注意起关键作用的物体,接着寻找解决方法的核心来寻找模式的。这可不是一个简单的过程,但一旦你发现了一些很好的模式,他们就成为很有价值的东西了。对我来说,他们的重要性在于能写一本书,供大家参考。你不必整本书地去啃,也不必将每本模式的书都通读一遍,来发现到底哪个有用。只要读到你认为可以认识到什么是模式,他们被用来解决什么样的问题,以及它们是如何实现的就行了。也没必要对所有模式都了如指掌。你仅仅需要的是,在你碰到某一个问题时,能够在书中找到对应的模式就足够了。只有那样,你才需要去真正的理解模式及其解决方法。

一旦你需要模式,那么你接着就必须去确定如何将它应用到你的系统中。模式中最关键的一件事是:你绝对不能盲目地去应用,这就是为什么模式工具遭致悲惨命运的原因。我喜欢用“半生不熟“这个成语来修饰模式――意味着你常常不得不把他们拿到你的项目里进行加工。每次我使用模式时:我都会这儿修饰一点那儿修饰一点。所以你会经常看到相同的解决方法,但却很少是一模一样的。

每个模式都是相对独立的,但是他们彼此之间并不是相互孤立的。所以经常一个模式会涉及另一个,或者一个模式经常会依赖另一个模式的存在。如果在你的涉及中有一个Domain Model的话,你只要看看Class Table Inheritance 就行了。模式间的界限自然也是非常模糊的,但我已经尽力将每个模式保持个性鲜明 .所以如果某人说“使用一个Unit of Work “你可以在书上查到,看看是怎么应用的,不必去阅读整本书。

如果你是一位富有经验的企业级应用设计人员,你可能也会发现大多数模式你都非常熟悉。我想你不必过分失望(我在序言中已经提醒过你)。模式现在并不是原先的思想了,而是对领域里所发生的一切通过大量的观察得到的结果。模式的作者也不会说他们在“创造”一个模式,而是说“发现”一个模式。

我们的角色仅仅是注意常见的解决方法,寻找其核心,将结果记录下来归为模式。对一个有经验的设计者来说,模式的价值不是在于它给你灌输了什么新的思想,而是促进和你的思想进行交流。如果你和你的同事都知道什么是Remote Facade,你们遍能够交流了,诸如说“这个类是一个Remote Facade“。同时也允许你对新手说,”使用一个Data Transfer Object 来处理“,他们就会来查阅本书。这样模式构成了一套通用的设计词汇,这就是为什么命名是那么重要。

The Structure of the Patterns //模式的结构

每个作者都必须借鉴前人的模式。有些人以经典的模式书籍为基础,像[Alexander et al, pattern language], [Gang of Four], or [POSA]。其它人就编辑自己的模式。我殚精竭虑思考如何组织才是最好的。一方面,我不想要GOF那么支离破碎的形式;另一方面,作为一本参考书,确实需要包含章节供别人参考。下面就是本书中我所用到的东西:

第一项就是模式的命名.模式的名称真是太重要了,因为模式的部分意图就是为了创建一套词汇供设计者们进行更为有效的沟通。所以如果我告诉你我的web 服务器是依靠一个Front Controller和一个Transform View 生成的,你都知道这些模式,你就会对我的web服务器架构有一个非常清晰的明确的认识。

接着的两项联系很大:意图和梗概。意图将模式用一两句话总结了一下。梗概是模式视觉上的表现,经常使用uml图式。使用他们的幕后想法是:作为一个简短的提醒—--模式是关于什么的,你就能够很快回想起来。如果你已经“拥有这个模式”,意味着你已经知道了这个解决方法,即便你还不知道对应的模式的名字,那么意图和梗概,就是你要去弄清楚的一切了,他们会告诉你模式是什么东东。

接下来的部分描述了一个激发模式的问题。模式可不是仅仅只解决这一个问题,但我认为他是一个最能激发模式的问题。

How it Works 描述了解决方法。在这里,我会安排一个讨论:它是如何工作的,我曾经碰到过的区别,以及随之而来的不同的实施的问题。讨论尽可能的独立于任何一种特定平台,我已经将平台细节部分进行缩减,你们可以看看也可以略过不看。在用得着的地方,我会使用uml图式来帮助解释他们。

When to Use It 描述了何时使用模式。在这里我谈到了如何进行遴选,采用这个解决方法而不是其他的。本书中,很多模式是可以二者择其一的,像Page ControllerFront Controller。没有哪个模式是放之四海皆准的,所以每当我发现一个模式时,我总问自己“我什么时候不这么做“,正是这个问题经常带着我走向另一个替代的模式。我喜欢加上一个或更多的例子。每个例子都是使用模式的简单的例子,以一些java or c#代码阐明。之所以选择这些语言,是因为好像绝大多数专业的程序员都能读懂这些语言。你得知道例子不是模式。所以当使用模式时,它未必就和这个例子完全相似—不要把它看作是美名远扬的宏指令。我有意的使这些例子尽可能的简单,我想你就可以清晰明了的方式看待模式了。在这期间,很多各种各样的重要的问题我都有意忽视了,当你使用时他们会变得非常重要,但这些是特定对你的环境的,这就是为什么你总是不得不对模式做一定的加工的原因。

这其中的一个结果就是,我已经竭尽全力去保证每个例子都尽可能的简单,同时还要阐明模式的核心信息。同样地,我常常挑选一个简单明了的例子,而不是一个演示了一个模式是如何同一个生产系统中要求的杂七杂八的东西进行工作的。这是一个在简单和过分单纯化之间投机取巧的平衡处理方法,但是同时太多实际的但很外围的问题会使理解一个模式的重点变得非常的困难。

这也是为什么我会去找一些简单的独立的例子,而不是一个有联系的实际运行中的例子。独立的例子孤立起来是很容易理解的,但在如何将他们组织起来的问题上提供很少的指导。一个有联系的例子会告诉你模式是如何组织起来的,但是如果你对其中所包含的其他模式不理解的话,就其中的任何一个模式来说理解起来也是非常困难的。虽然理论上,可以做出既联系又好独立理解的例子,实际上做起来很困难--至少对我来说,所以我选择了独立路线。

Fruther Reading 部分向你指出了这个模式其他讨论。这不是一本包罗万象的模式参考书。我将自己的参考限制在我所认为重要的地方,帮助你理解模式。对我没有阅读过的模式,我也没有讨论。同时我也没有提及那些我认为将会是很难找到的东西,或者是不稳定的web链接,在你还没有阅读本书之前恐怕就已经不见了

本书中不是所有的部分都以模式的形式表现出来。如果我没有想到一个好的例子或是动机文章,我就省去了。(本节完)

 

版权所有:UML软件工程组织