分布式架构进化之路_进化架构

在本系列的第一期中 ,我提出了一些有关软件世界中体系结构的定义。 但是,如果您一直在阅读整个系列文章(并且如果您不是我的母亲,我对此表示感谢!),您会注意到我将大部分时间都花在了设计上。 我这样做有两个原因。 首先,软件世界中存在许多体系结构定义(无论好坏),而新兴设计目前享有的声誉较少。 其次,设计中的许多问题都有具体的,语境较少的解决方案。 架构总是与组织内部的物理和逻辑基础架构进行大量耦合,因此很难单独讨论。

本期文章纠正了缺乏有关敏捷体系结构的材料。 在这里,我将讨论如何将体系结构与设计区分开来,涵盖一些广泛的体系结构考虑因素,然后通过有关版本控制端点的讨论深入到面向服务的敏捷体系结构(SOA)空间。

从设计中区分建筑

我最喜欢Martin Fowler对建筑的定义(通过与他的对话):

体系结构是以后很难更改的东西。 而且应该尽可能少的东西。

您可以将架构和设计之间的交互视为图1所示的关系:

图1.架构与设计之间的关系
分布式架构进化之路_进化架构_第1张图片

软件系统的体系结构构成了其他所有内容的基础,在图1中用灰色框表示。 设计元素位于体系结构的顶部,如红色框所示。 更基础的是,架构元素更难以移动和替换,因为您必须将所有元素移到它们之上以适应更改。 这种区别使识别设计与体系结构更加容易。 例如,您使用的Web框架是一个体系结构元素,因为它很难替换。 但是,在该Web框架内,您可以使用不同的设计模式来表达特定的目标,这表明大多数正式设计模式确实是设计的一部分,而不是体系结构。

Fowler对体系结构的定义的必然结果是,您应该构建体系结构元素,以便在确实需要时可以更轻松地替换它们。 但是如何确保呢? 这是一个例子。

许多框架试图诱使您使用它们的某些类,而不是JDK或开放标准组织(例如OASIS)附带的更通用的类。 这是耦合的裂纹经销商模型。 如果您屈服于诱惑,那么您将永远与框架相连。 这些框架采用的一般方法是,如果您使用它们的类,则可以使事情变得容易得多。 一个完美的例子来自Apache Struts Web框架(请参阅参考资料 )。

应用程序中包含业务规则和其他非基础结构代码的类是领域类 :它们包含有关您的问题领域的有趣信息。 Struts中包含的很酷的帮助程序类之一是ActionForm类。 如果您从ActionForm继承域对象,则应用程序中会发生各种魔术。 您可以从参数,自动验证(在Web和服务器层)以及其他方便的内容中自动填充表单字段。 您所需要做的就是继承Struts ActionForm类,如图2所示:

图2.使用Struts ActionForm
分布式架构进化之路_进化架构_第2张图片

在图2中 ,标为Model的框包括您的域对象。 它扩展了Struts的ActionForm ,使该结构以后很难更改。 如果将来某个时候您决定ScheduleItem也需要在Swing应用程序中工作,那您就下沉了。 您将得到两个同样不可口的解决方案:将所有Struts拖到Swing应用程序中(不要使用它)或将依赖项解结到Struts。

更好的选择是使用组合而不是继承,如图3所示:

图3.通过组合解耦您的域类
分布式架构进化之路_进化架构_第3张图片

在此版本中,域类(黄色)包括定义计划项目语义的接口。 原始ScheduleItem实现了此接口,该接口也由ScheduleItemForm实现,强制两个类的语义始终一致。 ScheduleItemForm又拥有ScheduleItem域对象的实例,并且所有ScheduleItemForm的访问器和更改器都传递到封装的ScheduleItem的基础访问器和更改器。 这使您可以利用Struts的出色功能,同时又可以保持与框架的分离。

经验法则是:让框架了解您是可以的,但让您了解框架不是可以的。 只要您可以保持这种关系,就可以防止将代码耦合到基础结构,从而使您可以更轻松地对体系结构和设计进行更改。 有时需要做更多的工作,但最终可以提高灵活性。 Struts不是提供这些诱人功能的唯一框架。 实际上,每个框架都包含一些帮助您将其耦合到框架的助手。 如果您发现自己是从域类中的框架或供应商处导入软件包,则可能会给自己带来未来的麻烦。

一些架构上的考虑

除了体系结构的定义外,在典型的企业环境中还产生了各种各样的问题。 我将在这里介绍其中一些敏捷方法。

建筑政治

当升任建筑职位时,企业政治是您遇到的第一个不礼貌的觉醒。 由于架构师通常是公司中最高的技术职位,因此,无论是好是坏,您都将成为IT部门中所有决策的发言人(和辩护人)。 实际上,通常您会为坏事而受到指责,而对好事却不会得到任何荣誉。 一些新兴的建筑师试图忽略这一点(当您处于技术战es时,这似乎工作得很好),但是在您的新职位上将不再起作用。

请记住,在大多数软件项目中,沟通比技术更重要。 如果您曾经遇到过失败的软件项目,请考虑失败的原因。 是由于技术原因还是由于某些通讯问题? 大多数时候,它是通信而不是技术。 技术问题有解决方案。 (有时它们是硬性解决方案,但始终有解决方案。)社会问题更加棘手,难以解决。 一个从书人件 (见名言的相关信息 )是:

总是一个人的问题。

即使对于您认为已经破灭的技术决策,政治也会动摇脑筋,特别是如果您发现自己可以批准购买企业工具的话。 (从好的方面来说,您可能会得到其中一个工具供应商的青睐,参加一场充满异国情调的高尔夫郊游。)请记住,作为一名建筑师,您不仅需要做出重要的决定,还需要做出一些明智的选择。 你也必须捍卫他们。 有时,您要与之交谈的人拥有自己的议程,这些议程在逻辑上不合逻辑,但在公司政治的严峻考验中确实有意义。 不要灰心,记住为什么要做出决定。

建立与购买

在大公司中出现的常见问题之一是决定是构建还是购买:根据当前要求,我们应该购买COTS(商用现货软件)还是自己构建? 做出此决定的动机是可以理解的-如果公司可以找到一些已经编写的软件来完全满足需要,则可以节省时间和金钱。 不幸的是,许多软件供应商都理解了这种愿望,并编写了可以定制的打包软件,如果它不能完全满足客户的需求。 他们有动力开发尽可能多的通用软件,因为它可能适合更多的生态系统。 但是它越通用,就越需要定制。 那是一群顾问出现的时候,有时需要数年才能完成所有自定义编码。

您是否应该购买COTS的问题实际上可以归结为另一个问题:该软件支持的业务流程是战略还是间接费用 ? 如果所讨论的业务流程仅仅是开销,那么购买COTS就是很有意义的。 这类软件的示例包括人力资源,财务和其他常见业务流程。 战略软件可在您的业务领域提供竞争优势。 不应轻易放弃竞争优势。

图4中的流程图旨在帮助您在构建和购买之间做出决定:

图4.构建与购买决策流程图
分布式架构进化之路_进化架构_第4张图片

在此流程图中,您必须做出的第一个决定是围绕战略和间接费用之间的重要区别。 如果需要是战略性的, 则应始终自己构建解决方案 。 如果您不这样做,则是在故意与竞争对手建立公平的竞争环境,而不是构建完全适合您当前和将来需求的产品。 打包软件吹捧其可定制性,但是可定制的数量有限。 如果您自己编写,则需要更长的时间,但是您拥有一个平台,您可以在该平台上构建与众不同的产品。

流程图中的第二个决定询问软件包是否立即可用。 购买软件包的一个常见陷阱是误解了将其转变为业务流程需要多长时间。 大多数公司对此判断有一个数量级。 您必须自定义的次数越多,花费的时间就越长。 更糟糕的是,一些公司允许其业务流程进行更改以适应该软件。 这是一个错误,因为无论好坏,您的业务流程应与竞争对手不同。

决策树中的第三步询问软件包是否可扩展而不是可定制 。 可扩展的系统具有定义明确的方法来扩展功能,而无需将任何东西砍入适当的位置。 这些扩展点包括定义良好的API,SOAP调用等。 自定义意味着您必须“欺骗”才能使程序包执行您想要的操作。 例如,如果您发现自己正在打开一个WAR文件,以便可以用另一个图像(必须将其命名为index.gif)替换名为index.gif的文件,则您正在自定义,而不是扩展。 试金石是您的更改是否有幸存下来的升级机会。 如果是这样,则说明您已经扩展了软件包; 如果没有,则说明您已对其进行了自定义。 自定义阻止您使软件包保持最新状态,因为您意识到要对新版本进行相同的更改需要花费很多精力。 因此,趋势是不进行升级,最终使您落后于最新的四个或五个版本,这使您有失去对正在使用的旧版本的支持的风险。

对某些企业而言,间接费用对其他企业而言具有战略意义。 例如,我为一家金融服务公司提供了一些咨询服务,该公司的聘用过程被认为是其关键的战略优势之一。 他们聘请最优秀的人才,花费大量时间和精力来寻找合适的人。 他们曾经要求我提供有关购买COTS人力资源系统的建议,而我却建议他们反对:为什么他们要与竞争对手建立起公平的竞争环境? 相反,他们接受了我的建议,并编写了自己的人力资源系统。 编写花费了更长的时间,但是一旦完成,他们就有了一个平台,可以简化竞争对手的劳动密集型工作。 招聘对于许多组织来说只是开销,但对于这家公司来说,这是战略性的。

在建筑中打字

SOA计划中经常出现的一个技术性更高(面向过程的较少)主题与分布式系统的类型化和版本控制有关。 这是这类项目中最常见的陷阱之一。 这很常见,这既因为它容易遵循工具供应商提供的路径,又因为它需要一段时间才能显现出问题,而且最棘手的问题是由于在项目的早期阶段不了解您所不知道的东西而引起的。

关于是否可以使用动态类型的语言来构建“企业”系统的争论被打死了,现在的争论几乎没有亮点。 但是,这种辩论为有关端点类型的分布式系统提供了重要考虑。 通过端点 ,我指的是两个不同系统之间的通信门户。 两种相互竞争的键入样式是SOAP,它通常使用诸如Web服务描述语言(WSDL)和表示状态传输(REST)之类的标准来实现强类型键入,后者倾向于使用更宽松的以文档为中心的类型(请参阅参考资料 )。 SOAP与REST的详细优缺点不在本文讨论范围之内。 在这里,我主要想谈谈在端点级别进行松散键入的好处,您可以使用两种样式都可以实现。

更具动态性的类型在端点上很重要,因为这些端点在通常以不同速度发展的系统之间形成了已发布的集成API。 您希望避免在这些系统之间紧密耦合特定的签名(类型和参数名称),这会使对话的任何一方都变得脆弱并且容易中断,从而妨碍了您分别对两个应用程序进行版本控制的能力。

这是一个例子。 在传统的SOAP样式集成中,您使用远程过程调用(RPC)类型的协议,并使用WSDL定义两个应用程序之间的对话的详细信息。 如图5所示:

图5.在应用程序之间使用RPC样式的调用
分布式架构进化之路_进化架构_第5张图片

RPC样式的集成使用WSDL进行“常规”方法调用,并将其抽象为SOAP。 因此,每个类都映射到WSDL中的类型,包括其所有参数的类型。 这种方法将对话的双方紧密地结合在一起,因为它们都依赖WSDL来定义发送了什么和期望了什么。 问题在于这个严格的定义。 如果您需要修改一个应用程序以采用不同的参数或更改现有参数的类型,而又不能同时修改两个应用程序,该怎么办? 您如何版本化端点? 几种方法都是可行的,但所有方法都存在严重的妥协。 例如,您可以使用新的WSDL定义创建另一个端点。 如果原始端点名为addOrder ,则可以创建另一个端点addOrder2 您可以看到这导致的黑暗地方。 很快,您将拥有数十个稍微不同的端点,到处都有重复的代码,可以处理一次性情况,因为很难预料人们一旦发布集成点将如何使用它。 您还可以使用通用描述,发现和集成(UDDI)(或仅是哈希图)之类的工具以具有端点分辨率的方式玩游戏,但这也不能很好地扩展。 根本问题是端点之间的紧密耦合,这阻止了端点以自然,独立的速度发展。

一种替代方法是将集成端点视为松散类型,如图6所示:

图6.在集成端点上使用松散类型
分布式架构进化之路_进化架构_第6张图片

通过将有趣的信息传递到文档中的端点,您可以在对会话的任何一方的主要和次要升级中都保持端点定义不变。 您可以选择灵活性,而不是依赖WSDL严格定义期望的内容。 现在,端点始终会包含一个文档,该文档封装了端点所需的事物类型。

要处理端点的版本控制,端点的第一步是解包文档,确定已传递的内容,并将其与期望的内容进行协调。 我通常结合使用Factory和Strategy设计模式(请参阅参考资料 )来确定我是否达到了期望,如图7所示:

图7.解压缩端点内的内容以确定类型
分布式架构进化之路_进化架构_第7张图片

端点的第一项工作是查看文档清单并确定其包含的内容。 然后,它使用工厂实例化将信息从文档中拉出的正确策略。 验证完所有部件后(如有必要,使用WSDL),将反序列化的对象传递给业务处理。

这种方法有几个好处。 首先,拥有一个具有两个正交作业的机制是一个坏主意,但这是传统RPC所假定的:端点既负责提供已发布的API进行集成,又负责验证类型。 由于它具有两种行为,因此您倾向于将代码混合在一起,从而使其难以理解和维护。 其次,您现在可以拥有该端点的任意数量的用户,所有用户都使用略有不同的版本。 只要有策略,就可以使用相同的端点支持任何版本(包括更新较慢的应用程序的旧版本)。 这使您可以根据需要进行更改,而不必担心强制企业中的其他应用程序跟上您的更改。 他们可以在自己的时间范围内更改和使用新的文档版本。

还没有可用的工具或框架来使您轻松地实现此方法,但是额外的一些前期工作可以提供上述好处。 您可以使用SOAP或REST来实现这种样式(尽管在REST中它更容易,因为REST本质上是以文档为中心的)。 通过创建松散类型的生态系统,您可以使不同的开发组按照自己的步调前进,从而使整个企业对应用程序的整体使用以最小的摩擦向前发展。 这就是进化架构的精髓:奠定基础,以最快的速度实现无摩擦的变更,而不会影响功能。

结论

架构是软件中的一个大而复杂的话题。 在本期中,我尝试涉及许多不同方面,从政治到SOA版本控制端点的实现细节。 在以后的文章中,我将充实关于架构的更多想法,以及一些新的架构方法来构建可演化的SOA,而无需向供应商支付数百万美元。


翻译自: https://www.ibm.com/developerworks/java/library/j-eaed10/index.html

你可能感兴趣的:(分布式架构进化之路_进化架构)