Developers and architects often ask me, “Why do I need service orientation?” My response is simple: scalability, maintainability, interoperability, and flexibility. In the past, distributed component technologies like DCOM tightly bound distributed components together. At the bare minimum, these distributed components had to share a common type system and often a common runtime. Given these dependencies, upgrades and software updates can become complex, time-consuming, and expensive endeavors. Service-oriented applications, in contrast, do not engender the same sorts of dependencies and therefore exhibit behaviors that better address enterprise computing needs.
Application requirements change over time. It has been this way since the dawn of computing, and there are no signs of this behavior slowing down in the future. Developers, architects, and project managers have gone to great lengths to apply processes to software development in hopes of regulating and controlling the amount and pace of change an application endures. Over the lifetime of an application, however, some of the assumptions made during the development process will certainly turn out to be invalid. In some cases, the resultant application changes will cause a cascading series of changes in other parts of the application. Autonomous, explicitly bounded, contract-based service-oriented applications provide several layers of encapsulation that buffer the effects of versioning one part of a system. In a service-oriented application, the only agreement between the message sender and the receiver is the contract. Both the sender and the receiver are free to change their implementations as they wish, as long as the contract remains intact. While this was also true of component architectures, the universal nature of service-oriented contracts further decouples the sender and receiver from implementation, thereby making the upgrade and version cycle shorter. Service orientation does not, however, remove the need for a good versioning process.
Every application has bottlenecks, and sometimes these bottlenecks can prevent an application from scaling to evolving throughput demands. Figure 2-4 shows an order processing Web site built with components.
Figure 2-4: A traditional component-oriented application
In this scenario, data retrieval might be the bottleneck. If that is the case, one way to scale the component-driven Web site is shown in Figure 2-5.
Figure 2-5: Scaling a component-oriented application
Essentially, we re-create the entire Web application on another server and use a load balancer to redirect requests to the least busy Web server. This type of scalability has proven effective in the past, but it is inefficient and costly, and creates configuration problems, especially during versioning time.
A service-oriented way to scale the order processing system in the Figure 2-5 example is shown in Figure 2-6.
Figure 2-6: Using services
Service-oriented applications can more easily scale the parts of the application that need to be scaled. This reduces total cost of ownership and simplifies configuration management.
Platforms change, sometimes dramatically, over time. This is true within any platform vendor, as patches and service packs, and ultimately new versions of a platform, are constantly being released. With distributed components, there is often a dependency on a platform component runtime. For example, how does an application architect know that a DCOM component will behave the same on servers running Microsoft Windows Server 2000, Windows Professional 2000, Windows XP, or Windows Server 2003? Since a DCOM component relies on the component runtimes on each of these systems, many testing scenarios appear seemingly out of thin air. When you start to think about testing within each possible configuration, service pack, and hotfix, your nose might bleed from anxiety.
Many of these problems disappear when applications become service oriented. This is largely due to the fact that messaging contracts are expressed in a platform-neutral XML grammar. This contract language decouples the sender from the receiver. The sender’s responsibility is to generate and send a message that adheres to the contract, while the receiver’s responsibility is to receive and process these messages. No platform-specific information must be serialized with the message, so endpoints are free to version their platform as they want. Furthermore, testing is much simpler, since each endpoint has to test only to the explicit service boundary.
The nature of service-oriented messages lends itself to routing scenarios that have been very difficult in the past. We can build some business rules around our order processing example for an illustration:
Orders can be for new items or repairs to existing items.
Orders for new items should ultimately be sent to the manufacturing system.
Orders for repairs should be sent to the repair system.
Both orders, however, must be sent to the accounting and scheduling systems before they are sent to their ultimate destination.
Service-oriented messaging applications are well suited for fulfilling these types of requirements. Essentially, routable information can be placed in SOAP message headers and used by any endpoint to determine a message path.
Many distributed systems secure communication at the transport level in a point-to-point manner. While the transmission event might be secure, the data transmitted might not be secure after the transmission. Log files and other auditing mechanisms often contain information that is secured when transmitted, and as a result, they are frequent targets of many security attacks. It is possible, however, using standard XML security mechanisms, to provide end-to-end security with service-oriented messages. Even if the message is persisted into a log file and later compromised, if the message was secured using one of the standard XML security mechanisms, the data in the message can be kept confidential.
When an initial sender sends a message to an ultimate receiver, the initial sender does not need to have a dependency on which platform the ultimate receiver is running. As you’ve seen with the binary message encoder, this is not always the case. Some message formats can introduce platform dependencies, but this is a matter of choice. In the purest sense, service-oriented applications are platform agnostic. This platform independence is a direct result of the universal nature of messaging contracts expressed in XML grammar. It is truly possible (not just theoretically) to send a message to an endpoint and have no idea what platform that endpoint is using. This resonates with businesspeople and managers because systems do not need to be completely replaced with a homogenous set of applications on a single platform.