这是Web Service设计方法系列文章中的第一篇,这个系列的文章重点介绍如何能够设计出在未来业务中拥有灵活应变能力的Web Service。
本篇内容
介绍
导航
范围
融化的奶酪
面向服务真的可以把奶酪彻底分开吗?
解决方案中的运动机件
松耦合,一种投资
合同
结论
介绍
这些想法集思广益的结果,首先是Beat Schweqler以及我们在去年在阿姆斯特丹举行的TechEd上做的名为“使用 面向对象技术实现服务的灰色地带”的演讲;以及Michael Regan,他对大量的使用者做过实地调查;最后是Christian Weyer,thinktecture的奠基人之一,他最近提出了一个新的工具环境叫做网络服务合同优先(Web Service Contract First, orWSCF)。我们大家的不同之处仅仅是Christian更多的关注那些懂得结构的受众,但是我们最初的努力是针对那些避开了结构的受众(点击这里浏览Christian的blog)。
导航
在接下来的这些文章中,我们将会引领您从高层次的抽象思考逐步具体化,最后给出切实可行的建议。同时,我们还将提供一个工具集来支持我们的建议,帮助您建立,提供并使用合同。我们希望这些工具可以从未WSCF的一部分或者变成一个全新的工具集。
• 第二篇文章—合同将会提供给您一个关于对话、合同以及约定的观点。我将发表我关于合同、代码编写、结构优先讨论上的一些个人观点。
• 在第三篇文章中,我将深入地探讨如何构建一个解决方案,展示一些在重用结构时您可能预想不到的问题并给出初步的建议
• 在第四篇文章中,我将展示一个工程的方案并设计一个可以灵活应变的服务结构。
• 在这个系列的第五篇文章中,将会展示前面那个工程的配套工程,并为其客户端设计结构
至于后面的文章,我们还没有太明确的计划到地写些什么。其内容应该会取决于我们在提供工具上的进度以及读者给我们的反馈。
范围
我们不想把焦点仅局限于 互联网范畴内的交互服务,在那里各种组织会根据各自的需要对合同进行随意的改动。我们接触到的大多数人,媒体和大型机构组织都希望了解如何才能制定出适合自身的,可以与时俱进的解决方案。在很多情况下,设计服务的机构都会拥有两个终端,它们至少会对这两方—服务使用者和服务提供者产生影响。我们希望可以帮助这些组织里的工程师和设计者们应付并尽量减少对其设计进行的改动时的复杂度。
在这个系列文章中,我们探讨了如何在一个面向服务的环境中对一个复杂系统进行变更的问题,并会就如何在媒体和大型组织中设计并构建服务给出指导。我们并非要试图为世界范围内的各种消费需要解决设计结构和协议的复杂性问题。这个系列文章并非什么万灵丹,但是它可以向您提供一些有用的指导,帮助您设计出更加灵活应变的解决方案。我们将从一个高层次的角度来审视这个问题,然后逐层深入,给出具体的结构和代码。为了配合这个系列文章,我们还发布了一些工具软件来支持我们的建议,我们希望这些工具可以融入到WSCF中。
融化的奶酪
不久前,我们摒弃了声名狼藉的意大利拉面式的过程化编程方式,从GOTO指令泛滥的“跳转纪元”进入了面向对象的世界,我称之为小方饺式的编程方式。这种编程方式的一大优点就是你对模块内部的改动不会对它的外部造成影响,就像隔着饺子皮看不出馅被换过一样。但是,一旦某个模块的功能发生了变化,它还是会影响到其他模块的,因为它们之间确实是存在联系的,这种关系就像煮在同一锅汤里的一个个饺子一样,汤汁把它们联系在了一起—我正是从这种汤汁联想到了融化的奶酪的这个形象。
动态链接库灾难就是一个反应这种融化奶酪效应的好例子。通常,多重应用会依赖于同一个动态链接库,只要其中的一个应用被更新,这个动态链接库就需要被相应地改动以适应这个应用的新版本,随着而来的是其他所有依赖于这个动态链接库的应用都需要进行改动。
面对那些前所未有的复杂系统,我们在如何对它进行改变的问题上有些束手无策,像动态链接库灾难这样的问题只是小巫见大巫。我们可以实现对系统的某个部分的改变,但却不知道如何协调各个部分之间的相互关系。这些不同的部分的更新换代速率不同,在不同系统上的应用次数也各异。其他系统的情况也好不到哪里去,而且它们不断地提出越来越复杂的需求。
松耦合是一种投资。了解并预估进行改动可能给系统带来的影响,并计算出这种变更会使组织者付出怎样的代价是非常重要的。它可以帮助你明确怎样的投资是可以接受的,而这个又可以反过来帮你确定设计的范畴和规模。变更须付代价由每次变更须付代价和变更频率决定。所以,对于经常需要变动的部分,考虑耦合方式就变得更加重要。在某些情况下,改变给其周围环境和相关软件带来的扩散式的影响往往造成更加巨大的,而且经常是不容易为人察觉的代价。
我们可以从以下几个不同方面考虑如何为变动做准备:
• 我可以修改软件以支持改变的功能吗?
• 我可以只修改软件的某些部分而不是对整体进行修改吗?这种局部性的修改可以细化到什么程度呢?
• 我可以应用修改后的版本吗?
• 我可以不用重新启动系统就应用修改后的版本吗?
• 我可以不用拆分系统就应用某些部分的改动吗?
• 我可以允许使用者有自己的更新日程并保证关键的业务功能不会因为更新进度不同而受到影响吗?
当你想改变一项服务的功能时,你不得不让它的消费方,客户方和其他互动服务也相应的进行改变。你可能处在一个比较幸运的环境中,可以同时实现服务方和用户方的改变,但是,由于各种各样的原因,这并不总是可能的,比如说:
• 你不能影响所有消费者(也就是说你可能可以改变一些消费者,但你无法改变所有消费者)。
• 你没有时间让所有的消费者/客户为变化最好准备,你只能先让一部分人使用变更后的版本。
• 你并不想把新功能一次性的展示给所有的用户,你想先在一部分用户中试行一段时间。
• 你不得不一点一点地进行变动,这可以由很多原因造成。
在上述所有情况中,新一批用户与先前的用户必须同时使用旧的服务版本。虽然可能不太常见,但相反的情况也是有可能发生的,那就是一个用户需要同时与多个版本的服务进行交互,比如说当从各种子服务中整合信息的时候。
版本更新的一个主要目标就是从一个稳定的情况顺利过渡到另一个稳定的情况。了解版本更新可能对合同造成的影响可以使我们区分哪些部分是可以共同工作的,哪些是不能的。
合同
我并不认为使用者同时依赖于动态链接库的接口和它的底层实现是造成动态链接库灾难的一个原因。组件技术提供给我们一个明确的接口和底层实现的划分,使得使用者只依赖接口,而不依赖于底层实现。但是使用它的 程序员利用底层实现可以更轻松的实例化一个对象而无需借助于对象制造厂。对于服务方(编写组件)的程序员,可能更愿意选择利用相同的统一标识符(GUID)轻松地实现一个并不兼容的改动。在组件技术中,统一标识符(终端)通常是和底层实现联系在一起的。较少有开发者会为单独的类提供出各种额外的接口,以备将来可以不必破坏原有应用就进行更新。
所以不管是使用动态链接库还是服务(我们已经解释过面临的问题都是一样的),与底层实现的绑定才是问题的关键所在。我们坚信这个问题的解决方案存在于合理定义的合同结构以及使用所谓的“合同优先设计方法”。
为了避免对工程造成的影响,我们必须在设计的不同阶段采取以下方法:
用户需要:
• 与合同进行绑定
• 使用配置(制造厂)获得终端访问入口
服务方需要:
• 为重大改动提供新的合同和终端,最好是在保留原有合同和终端的基础上
• 在不影响合同和终端的基础上完成对底层实现的替换更新
将服务方视为一个黑盒子:
• 服务方为每一个合同提供了一个终端。进行一个与以往合同不兼容的改变需要增加一个新的终端,也就是提供一个新合同的访问入口,此时,最好保留旧的终端。
• 客户绑定到一个终端上
但是,用户与某一终端的绑定并不需要代码编写,更好的方式是使用配置。这使得我们可以移动终端甚至对使用某一特定终端的用户进行搜索。