Understanding Service-Oriented Architecture
Another solution is to pass in an argument that directs the service whether to return address information. A BankAccountService would have only one GetAccountHolder method. The developer would add an additional argument to the method, to instruct the service whether to return address information as well. Consumers who need only account-holder information could pass in the proper switch to retrieve it. Users who need address information as well could pass in the proper switch to retrieve both.
But what if consumers need only zip codes for all account holders? They would have to retrieve both account-holder information and address information and extract zip codes from a very large message. What if consumers pass in the list of attributes in which they're interested?
This sophisticated alternative implements an interface that accepts a list of attributes to return to the consumer. Instead of sending the account number and an address indicator, consumers submit a list of all of the attributes to return. The list may contain just first and last names or may include all or portions of the address data, such as city and street address. The service would interpret this list and construct the response to consumers to include only the data requested. This solution minimizes both the number of trips consumers make to the service and the amount of data that must travel the network for each request. Figure 13 illustrates this option.
Figure 13 A method that returns just the attributes requested.
This approach has two downsides. The first is that the request message will be larger than any of the previous solutions, because the consumer must send the request data as well as the data map on each request. If all service consumers need the exact same data from the service, this solution would perform worse than the previously discussed alternatives.
Second, this solution is also more complex to implement for service developers, and service consumers might find the interface more difficult to understand and use. To alleviate this problem, a service proxy could wrap the complexities of the service interface and provide a simple interface for consumers. A consumer would use multiple distinct and simple service methods on the proxy. The methods map to the way the consumer wants to use the service. The proxy would internally map these multiple methods into a single service-request interface format that accepts a map of data to return. The advantage of this technique is that it allows the service to support any granularity, while providing specific granularities to consumers based on their domain understanding.
If these implementations are not possible, it is always better to return more data, to minimize network round trips, because future clients are likely to need the data. It is also possible to implement several of these options, to solve the needs of multiple consumers. However, this increases the effort to maintain the service and also detracts somewhat from the service's modular understandability.
A service's ability to have multi-grained methods that return the appropriate amount of data is important to reduce network traffic. Extra network traffic is due either to excessive unnecessary data or to a large number of requests to get data.
Granularity is a difficult problem to reconcile when designing service interfaces. It is important to understand the options and implement the most appropriate interface. In the past, arguments surrounding service interfaces have focused mainly on determining the right granularity. Services actually require the designer to find the right granularities for service consumers.
Location transparency is a key characteristic of service-oriented architecture. Consumers of a service do not know a service's location until they locate it in the registry. The lookup and dynamic binding to a service at runtime allows the service implementation to move from location to location without the client's knowledge. The ability to move services improves service availability and performance. By employing a load balancer that forwards requests to multiple service instances without the service client's knowledge, we can achieve greater availability and performance.
As mentioned earlier, a central design principle in object-oriented systems is separation of implementation from interface. This means that an object's interface and its implementation may vary independently. The primary motivation for this principle is to control dependencies between objects by enforcing the interface contract as their only means of interaction.
Service-oriented architecture takes this principle one step further, by reducing the consumer's dependency on the contract itself. This reduced dependency through the use of dynamic binding also has the effect of making the service's location irrelevant. Because the service consumer has no direct dependency on the service contract, the contract's implementation can move from location to location.
A service's composability is related to its modular structure. Modular structure enables services to be assembled into applications the developer had no notion of when designing the service. Using preexisting, tested services greatly enhances a system's quality and improves its return on investment because of the ease of reuse.
A service may be composed in three ways: application composition, service federations, and service orchestration.
An application is typically an assembly of services, components, and application logic that binds these functions together for a specific purpose. Service federations are collections of services managed together in a larger service domain. For example, a checking account service, savings account service, and customer service may be composed into a larger banking-account service. Service orchestration is the execution of a single transaction that impacts one or more services in an organization. It is sometimes called a business process. It consists of multiple steps, each of which is a service invocation. If any of the service invocations fails, the entire transaction should be rolled back to the state that existed before execution of the transaction.
For a service to be composed into a transactional application, federation, or orchestration, the service methods themselves should be subtransactional. That is, they must not perform data commits themselves. The orchestration of the transaction is performed by a third-party entity that manages all the steps. It detects when a service method fails and asks all the services that have already executed to roll back to the state that existed before the request. If the services have already committed the state of their data, it is more difficult for the method to be composed into a larger transactional context.
If the service cannot be subtransactional, it should be undoable. Especially when dealing with legacy systems, it is sometimes impossible to execute a function within the context of a transaction. For instance, consider an older system that manages checking accounts. The service is a fagade for the legacy application. When the service receives a request to deposit money into a checking account, it puts the request into a queue. The legacy system reads the request from the queue and executes it. It is difficult to make this request subtransactional, but it can be undoable. If the deposit transaction is composed into a larger transaction and another step in the larger transaction fails, the checking account deposit transaction can be undone by withdrawing the same amount from the checking account. While to a developer this makes perfect sense, a customer would probably see the deposit and withdrawal transactions on his or her statement at the end of the month, so it should be used with care.
With the size and complexity of modern distributed applications, a system's ability to recover from error is becoming more important. A self-healing system is one that has the ability to recover from errors without human intervention during execution.
Reliability measures how well a system performs in the presence of disturbances. In service-oriented architecture, services will be up and down from time to time. This is especially true for applications assembled from services from multiple organizations across the Internet. The extent to which a system is self-healing depends on several factors.
Reliability depends on the hardware's ability to recover from failure. The network must also allow for the dynamic connection to different systems at run-time. Modern Internet networking protocols inherently provide this capability.
Another aspect of self-healing is the architecture from which the application is built. Architecture that supports dynamic binding and execution of components at runtime will be more self-healing than one that does not. For instance, service-based systems are self-healing to a greater degree than previous architectures, because services are bound to and executed dynamically at runtime. If a service fails, the client may find, bind, and execute a different service, as long as the other service provides the same or a similar interface contract.
In addition, because service-based systems require that the interface be separate from the implementation, implementations may vary. For instance, a service implementation may run in a clustered environment. If a single service implementation fails, another instance can complete the transaction for the client without the client's knowledge. This capability is possible only if the client interacts with the services interface and not its implementation. This property is fundamental to all service-oriented architectures.
Page 7 of 8