Classes, objects, components, and interfaces are the basic building blocks of modern software. Some of these elements encapsulate problem domains, while others provide system infrastructure and technical architecture. Each building block provides a useful function, but the real power lies in the composition of individual elements into a collaborative solution that provides tangible business value to an enterprise (or a web of connected enterprises). To enable this level of collaboration, software elements must adhere to agreed-upon organizing principles and must expose standard interfaces to each other. Where components are dissimilar, one element must be adapted to the other, or both must be adapted to an agreed upon standard.
Chapter 5 introduced the notion of instance-based and service-based collaboration, highlighting the strengths and weaknesses of each approach. Service-based collaboration works well in scenarios where the consuming application does not have any control over the remote services or has to interoperate with solutions developed on top of different programming languages or platforms.
Service-based interfaces expose a single instance of an interface that provides a service to potential consumers. In the context of Web services, Microsoft defines a software service as a “discrete unit of application logic that exposes message-based interfaces suitable for being accessed across a network.“ [Microsoft02-2]
A service does not depend on the process that invokes it; it is self-contained and context-independent. This allows any potential consumer on the network to access the service. Services are well-defined by means of a contract that specifies the format for requests to the service and the format of the associated replies.
Although not necessarily message based, the notion of creating a set of logically grouped services was used in application development before the advent of distributed applications. For example, operating systems provide services to all applications running on the operating system. The Microsoft Windows GDI library, for instance, provides graphical services, and the Open Database Connectivity (ODBC) API exposes database access services. And just as abstracting some of the core capabilities of an operating system in to a set of services helped simplify application programming models, identifying core business capabilities of an enterprise and encapsulating them as a set of interoperable services helps to simplify collaborations with partners outside the corporate firewall.
Service-oriented architectures (SOAs) apply the concept of a service to distributed enterprise applications. In an SOA, each application exposes high-level business functions as services to be consumed by other applications. Because of the expanded scope and complexity of these service-oriented solutions, a service-oriented architecture must provide additional functions beyond the capability to invoke a remote service. The most important of these functions include:
Making services locatable at runtime. It is easy for a stand-alone application to locate an operating system service such as a GDI call; it is implemented in a local dynamic-link library named gdi32.dll. However, enterprise services can be distributed across many computers, networks, or facilities. Some of these services may change locations because they are tied to existing applications. Therefore, locating a service in a distributed, service-oriented architecture can be a complex task.
Making service and consumer agree on a common format. After the correct service is found, the consuming application must be able to dynamically determine which protocol to use to access the service, how to format a request, and what type of response to expect. Because services can be implemented in a variety of languages and platforms, getting the service and the consumer to agree on a common format can also be a challenging task.
When one method calls another inside an application, the method signature defines the “understanding” between the method and the caller, for example the number and types of parameters passed into the method and returned on its completion. Method calls can embody their understanding in a simple method signature because the caller and the method make a number of implicit assumptions; for example, that both methods execute inside the same process and share the same memory space; that both methods use the same programming language; and that execution returns to the calling method once the called method is complete. In the world of distributed SOAs, many of these assumptions are no longer valid and need to be spelled out explicitly in a service contract.
The service contract must specify the implementation of the communication channel connecting the Service Consumers with the Service Provider Applications, such as the network protocol. The service contract must also specify what kinds of messages the service can consume or produce, described by means of a detailed schema for each message involved in the interaction.
Figure 6.1 shows the elements of a service-oriented architecture.
Figure 6.1: Invocation of a service in an SOA
A single service may need to support multiple contracts. For instance, service consumers within the same organization may want to interact with the service through a series of relatively fine-grained messages and may be granted access to sensitive functionality. Service consumers external to the organization may want to interact with the service in a more coarse-grained manner for performance reasons, and will not be granted access to sensitive functionality.
The following steps are required to invoke a remote service:
Discovery: A service consumer (any application wanting to access a service) queries the service repository, which provides the location of the desired service.
Negotiation: The service consumer and the service provider agree on a communications format specified by the service contract.
Invocation: The service consumer invokes the service.