本系列教程五部曲中的第 3 篇文章,介绍基于 SOA 的 Web 服务如何被实际实现。该服务实现首先要决定什么组件将提供什么服务。该决定对于服务可用性、分发、安全性、事务范围和耦合具有重要的含义。在这些决定做出之后,您就能够对每一项服务的功能性是如何实现的,以及被要求的服务是如何被实际使用的进行建模。然后,您能够使用包含在
IBM® Rational® Software Architect 中的 UML 到 SOA 的转换特性,创建一个能够在
IBM® WebSphere® Integration Developer 中被用来实现、测试和配置完全解决方案的一个
Web 服务实现。
在本系列的第 1 篇文章 “第
1 部分 服务识别” 中,我们描绘出了识别那些同业务需求相连接的服务的一种方法的大致轮廓。我们首先捕获实现业务任务所必须的业务目标。然后,对满足这些目标所必须的业务操作和过程进行建模。接着,我们将业务过程作为一个帮助我们识别被要求的服务以及它们之间潜在联系的合同来使用。
在第 2 篇文章 “第
2 部分 服务规范” 中,我们对服务规范的细节进行建模。服务规范定义了服务的一个潜在消费者需要知道的所有东西,以便他们决定是否有兴趣使用该服务,以及如何使用该服务。它同样指定了一个服务提供者必须知道的所有东西,以便他们成功地实现该服务。也就是说,服务规范是消费者需要的东西和服务者提供的东西之间的沟通媒介或者合同。这一信息在服务规范中被捕获,使得搜索资产库中的可复用服务并且获得所有必要的信息变得很容易,而且无需在许多不同的文件中查找或者搜索相关的元素。
在本篇文章中,我们将看到服务是如何被实际提供的,或者按照 Unified Modeling Language (UML) 的术语,是如何被实现的。服务实现的设计首先要决定什么组件将提供什么服务。该决定对于服务可用性、分发、安全性、事务范围、和耦合具有重要的含义。在这些决定做出之后,您就能够设计每一项服务的功能性是如何实现的,以及被要求的服务是如何被实际使用的。
在本系列的下一篇文章 “SOA 建模 第四部分 服务组成” 中,将会描述这些服务如何能够被组合在一起来创建新的服务。最后一篇文章
“第五部分 服务实现”,将会使用 IBM® Rational® Software Architect 中的 UML
到 SOA 的转换特性,创建一个能够在 IBM® WebSphere® Integration Developer
中被直接用来实现、测试和配置完全解决方案的一个 Web 服务实现。
全面理解 SOA 建模,需要知道服务是如何被提供者实际实现和被消费者实际使用的相关细节。如果实现很困难,那么可能是由于规范不正确或者识别了错误的服务。本文显示了如何对我们在前面文章中设计的每一项服务规范的实现进行设计。该实现设计包括以下三个步骤:
- 决定哪一位提供者提供哪一种服务
- 设计服务实现
- 集合并连接必要的服务消费者和提供者,对完全的实现进行建模
决定哪一位提供者提供哪一种服务(可能不止一位)是由许多因素驱动的,包括:
- 服务最有可能被应用在哪里
- 它们最有可能被部署在哪里
- 服务提出什么质量要求
- 功能性区域的稳定性
- 预期的最大变化在哪里
- 在域中可以容忍多少耦合
- 安全性问题
- 可应用的平台实现技术
- 现存系统的集成和复用
所有这些相关内容的详细分析已经超出了本文的范围,但是它们都被包含在 IBM® Service Oriented Modeling
and Architecture (SOMA)方法中。在此,我们将假设
IT 架构师已经决定了哪一位服务提供者将会提供什么样的服务,所以我们能够将精力集中在提供者是如何被建模和组合到消费者解决方案中的。
同本系列教程中的所有文章一样,我们将使用现已存在的 IBM Rational 和 IBM WebSphere 工具来建造解决方案,并且将它们链接到一起,从而我们能够根据需求验证该解决方案,并且更加有效的对变化进行管理。表1总结了我们将要开发例子的过程,以及我们将要使用的工具。
表1. 开发过程角色、任务和工具
角色 |
任务 |
工具 |
业务执行官 |
传达业务目标 |
IBM® Rational® RequisitePro® |
业务分析师 |
分析业务需求 |
IBM® WebSphere® Business Modeler |
软件架构师 |
设计解决方案的体系结构 |
IBM Rational Software Architect |
Web 服务开发人员 |
实现该解决方案 |
IBM® Rational® Application Developer
和 IBM® WebSphere® Integration Developer |
首先,我们回顾在前面文章中被识别和指定的服务规范。图1显示了我们的解决方案所必须满足的业务服务需求合同。
图1. 服务需求合同
回想这个服务协作反映了一个需求合同,它潜在来源于一个反映我们的服务解决方案所必须做的事情的业务过程中。它是一个和体系结构无关的需求规范,并不过度约束
SOA 解决方案。
图2显示了作为满足需求所需要的被识别的服务规范。本质上讲,我们正在建造(购买、复用、适应)能够在这一业务服务需求合同中扮演角色的服务。
图2. 服务拓扑结构
图3显示了完全的 Scheduling 服务规范。由于没有使用时序安排服务的协议,该服务规范只是一个简单的接口。
图3. 时间进度服务规范
图4显示了 ShippingService 服务规范。
图4. ShippingService 服务规范
这一服务规范稍微有些复杂,这是因为它是通过使用简单的回调式的交互对交付者和订购者之间的交互进行建模。由于该规范包括行为协议,所以使用一个对服务协议中涉及到的角色(属性)进行定义的抽象类来进行建模。这些角色的职责都被它们的类型所定义,这些类型就是由服务规范提供的或者要求的接口。ShippingService
服务规范所拥有的 ShippingService 交互作用,定义了交付者和订购者如何进行相互作用的规则。这一交互作用将成为连接到该服务接口所定义的一个服务的服务信道的合同。
图5显示了 InvoicingService 服务规范。
图5. InvoicingService 服务规范
这一服务规范稍微有些复杂,这是因为被提供和被要求的服务的功能性必须以一个指定的顺序被调用和响应。在这种情况下,一个活动习惯于定义服务协议。然而,这只是一个选择问题;我们可以使用交互作用或者状态机。
图6显示了 Purchasing 服务规范:
图6. Purchasing 服务规范
Purchasing 服务规范反映了在初始的 Process Purchase Order 业务过程中被指定的功能性。它反映了一个被指定和设计的服务来实现业务过程。由于没有于这一规范相关联的服务协议,所以我们再一次使用套用的接口对其进行建模。
至此,我们已经准备好设计提供给每一项服务的组件。
服务定义了一组由服务提供者所提供的功能,这些功能满足了服务消费者或者用户的需要。服务实现设计的第一步,就是供应服务。也就是说,指明哪一个服务提供者将提供什么服务功能。这是设计
SOA 的关键部分,这是因为选择提供者建立了服务消费者和提供者之间的关系。因此,这就建立了系统的功能以及各部分之间的耦合。
您可以将所有这些操作放入到一个单一的服务中,拥有一个单一的解决方案。但是所有客户端都将依赖于一个服务,这就将导致一个非常高度的耦合。提供者的任何改变都将导致所有消费者的改变。这是早期
C 编程时代里常见的模块库问题。您也可以为每一项功能创建一个独立的服务,但是这就将导致一个非常复杂的系统,并且它不会表现出很好的封装性和内聚性。它也很难在共同遵循一个协议来使用一组相关功能的消费者和提供者之间进行建模。
最后,决定服务参与方需要一些技巧,会被许多因素所影响和制约。分发能够扮演一个关键的角色。如果我们能够设计出独立于消费者和提供者位置的
SOA 解决方案的话,将非常有意义,但是通常来说这并不现实。服务被配置在哪里,是同消费者和其他被需要的服务相关的,并且对解决方案的性能、适应性、和安全性具有非常重大的影响。在解决方案的体系结构中忽略这一点,可能导致不可接受的解决方案的实现。
此处,我们的问题相当简单,所以决定服务参与方将提供或者消费什么服务并不十分困难。下一小节将提供图2中所显示的所有服务的提供者的模型,以及图下面详细的服务规范。这是一个相当简单的例子,许多服务提供者仅仅提供一项只具有一种功能的服务。这种情况并不常见。服务提供者通常要提供或者消费(或者两者皆是)许多服务,每项服务具备许多种功能。我们的例子只是关注概念,而不去纠缠例子本身的细节。
货品计价
货品计价服务提供者为计算订货单的初始价格提供 Invoicing 接口,当交付信息得到之后,它重新定义这一价格。订购者的全部价格取决于产品在哪里生产,以及它们从哪里被交付。最初的价格计算可能被用来验证消费者有足够的信用或者仍然想要购买该产品。在完成定购前对此进行验证显然要好的多。
我们对服务提供者进行的设计,首先创建一个定义其需求的系统用例,以及一个被称作 Invoicer 的实现该用例的
<serviceProvider> 组件。货物组件将会在信用包中,该包导入 CRM (消费者关系管理)来使用普通服务数据(消息)定义。图7显示了我们所做的所有工作。
图7. 初始的 Invoicer 服务提供者
Invoicer 服务提供者将提供 InvoicingService 服务。我们通过添加一个 <service>
端口到 Invoicer 中(它的类型是 InvoiceService 服务规范)来对其进行建模。所有的服务都由服务规范输入,这些规范定义了哪些功能性被提供和要求,以及使用它们的协议。图8展示了添加了货品计价服务的
Invoicer。
图8. 添加一个 InvoicingService 到 Invoicer 服务提供者
我们能够通过服务的类型看到它提供了 Invoicing 接口并且需要 InvoiceProcessing 接口。从服务的类型中,我们知道连接到服务的消费者要使用该服务必须做什么,以及
Invoicer (或者任何其他的提供者)要实现它必须做什么。服务的任何使用和实现必须同服务的规范及其协议相一致。
货品计价 <service> 端口也能够为各组件相连接的使用,指定由 Invoicer 组件所提供的可能的绑定。不同的可能的绑定方法,例如
IIOP 之上的 RMI (Internet Inter-ORB Protocol 之上的 Remote Method Invocation)
或者 HTTP 之上的 SOAP,能够再一次对服务的性能、可用性和安全性产生深刻的影响。因此,这些关注应当作为服务设计的一部分,即使它们可能是针对特定平台的。至少,解决方案设计应当处理服务连接器是本地的、远程的、还是两者皆是。
Invoicer 提供 Invoicing 接口,它涉及到两个操作:
- initiatePriceCalculation
- completePriceCalculation
Invoicer 必须提供一个用于每一项服务操作的实现或者方法的设计。该方法还必须在价格计算被完成时,调用 InvoiceProcessing
接口的 processInvoice 操作。图9显示了 Invoicer 组件拥有的两个在被提供的操作中有同样名称的行为。
图9. Invoicer 服务接口
completePriceCalculation 活动是用于 Invoicing::completePriceCalculation
服务操作的方法。它使用不透明的行为来计算合计价格,然后调用货品计价端口的 InvoiceProcessing::processInvoice
操作。(processInvoice 行动的目标输入插口就是货品计价端口。)请注意这同 InvoicingService 服务规范所指定的货品计价协议是一致的。
initiatePriceCalculation 不透明行为是用于 initiatePricesCalculation 服务操作的方法。该操作通过使用从不透明行为的代码体中捕获的
Java™ 代码来实现。
生产时序安排
生产时序安排服务提供者提供 Scheduling 接口以决定何时何地生产这些货物。这一信息能够被用来创建一个在处理定购单时被使用的交付时间表。
Productions 组件通过其时序安排端口提供 Scheduling 接口,如图10所示。<service>
端口指定了该端口上可以提供的绑定风格。
图10. Productions 服务提供者
服务操作方法是 requestProductionsScheduling 和 sendShippingSchedule 行为。没有被显示在图表中的实现细节,可能被留给开发人员使用更加适用的、特定平台的语言来实现。
交付
一个交付服务提供者将交付货物的 Shipping 接口提供给消费者来填满订单。它还需要 ScheduleProcessing
接口来要求消费者处理完成的时间表。
图11显示了 Shipper 服务提供交付服务作为 ShippingService 服务规范所指定的内容。
图11. Shipper 服务提供者
变形的类型
要使用前面显示的服务提供者设计,我们需要理解消费者和提供者之间的关系:它们是如何被连接的,如何在连接器的终端指定端口的类型,以及如何决定那些类型是否兼容并且导致有效的连接。
SOA 就是将提供者的功能同消费者的需要相匹配。消费者一方拥有通过请求满足的需要,提供者一方拥有通过服务提供的功能。通过将请求和服务相连接,消费者的需要就被映射到提供者的功能上面。消费者使用服务的方法和提供者提供服务的方法,必须同服务和请求规范中的定义的协议相一致。
当一个服务消费者端口(一个请求)被连接到一个提供者的服务端口时,连接器终端的端口必须一致。这意味着消费者的请求必须要求提供的端口接口,而且消费者必须提供被要求的端口。进一步,消费者和提供者必须依照服务接口中定义的协议进行相互作用。
请求端口能够使用任何类来定义它的类型。它能够提供或者要求(或者两者皆是)任何接口,只要它们同与其相连接的任何服务端口都兼容即可。也就是说,消费者和提供者直到它们被连接之前都是分开的,并且并不要求它们拥有一个同被提供的服务规范相一致的特定的服务规范。
然而,在许多情况下,消费者请求的类型正是它所连接的服务端口的补足物或者变形。结果是我们可以方便的定义一个约定,从而易于指定和指明一个能够被用作消费请求端口类型的服务规范的变形。这一类型就是服务规范的变形。服务规范同它的变形之间的唯一区别在于,被提供(实现)和被要求(使用)的接口是相反的。类中的其它所有事情都是一样的,包括对服务协议进行建模的行为。
例如,请考虑 InvoicingService 服务接口。图12显示 InvoicingService 的变形,被称作 ~InvoicingService。
图12. ~InvoicingService,即 InvoicingService 的变形
这里所使用的会话是为了将变形命名为同服务规范一致的名字,除了以符号 ~ 开头之外。任何命名协定都可以被使用,但是应当前后一致,以便我们识别这些类型之间的关系。~InvoicingService
同 InvoidingService 具有相同的内在结构和行为。它仅仅是使用了 InvoidingService 所提供的接口,并且实现了被要求的接口。
用于 UML 的 IBM Software Service Profile 的一个进一步扩充,可能介绍从功能中区别需要的请求端口的概念。然后,请求端口能够使用同样的类型作为服务端口,由此就不再需要定义变形的类型。请求端口将指示出即将被适用的服务的性能,而服务端口将指示出它们将要被提供。
至此,我们已经完成了服务、消费者和提供者需要满足业务目标的识别、规范和实现(或者称为实现)的设计。结果就是得到一个架构的服务解决方案的和技术无关的设计模型。但是我们仍然没有创建一个能够将
Invoicer、Productions 和 Shipper 所提供的服务集合在一起,并且使用它们处理一个定购单的服务消费者。在本系列教程的下一篇文章
“SOA 建模 第四部分 服务组成” 中,将会显示如何将这些服务提供者连接起来,并且精心设计它们之间的交互作用,从而为业务需求提供一个完全的解决方案。
学习
获得产品和技术
讨论 |