By using WCF, we can quickly build services that we can integrate into any enterprise solutions. Apart from writing entirely new functionality, we can use WCF to implement services that wrap existing applications, and connect them together in ways that were previously difficult to achieve. WCF can act as the “glue” for combining applications and components together. Additionally, WCF can make use of standard protocols, data formats, and communications mechanisms, enabling interoperability with services developed by using other technologies. WCF is an ideal platform for implementing a Service-Oriented Architecture, or SOA.
An SOA consists of a set of resources on a network that are made available as independent services, and that can be accessed without requiring any knowledge of how they are implemented. We can combine the services in an SOA to create an enterprise application. I don’t want to go into the full theory of SOA, but the main benefits are that it enables us to create complex solutions that are independent of any specific platform and location. This means that we can quickly replace or upgrade a service or move a service to a different site (possibly running on faster hardware), and as long as the service exposes the same interfaces as before, we can continue to use it without needing to modify any code. However, SOA is not a magic wand that will instantly solve all of our distributed application architecture problems. To successfully design and implement an SOA, we should be aware of what has become known as the “Four Tenets of Service Orientation.” These are:
- Boundaries are explicit. Applications and services communicate by sending messages to each other. We should not make any assumptions about how a service processes a request or how a client application handles any response to a request. Following this principle can help to remove dependencies between services and client applications. Additionally, sending and receiving messages has an associated cost in terms of communications. We should design the operations that services implement with this in mind, and ensure that clients call services only when necessary.
- Services are autonomous. If we are building an application based on services, we might not have control over every service we are using, especially Web services hosted outside of our organization. The location of a Web service might change, or a service might be temporarily taken off-line for maintenance or other reasons. We should design our solutions to be loosely coupled, so that they can tolerate these changes and continue running even if one or more services are unavailable.
- Services share schemas and contracts, not classes or types. Services publish information about the operations that they implement and the structure of the data that they expect to send and receive. Clients use this information when communicating with the service. We should design contracts and schemas to define the interfaces that our services expose. This can reduce the dependencies that clients have on a particular version of our services. Services can change and evolve over time, and a new version of a service might appear superseding a previous version. If a service is updated, it should maintain compatibility with existing clients by continuing to implement existing contracts and send messages that conform to existing schemas. If we need to modify a service and provide additional functionality, we can add contracts and schemas that extend the original capabilities of the service while retaining the existing contracts and schemas. Older client applications should continue to work unchanged.
- Compatibility is based on policy. The schemas and contracts exposed by a service define the “shape” of the service but not the nonfunctional requirements that a client attempting to access the service must fulfill. For example, a service might have security requirements that state that clients must connect to it in a particular manner and send and receive messages by encrypting data in a specific way. This is an example of policy. The policy requirements of a service cannot be specified by using contracts and should not require additional coding on the part of the client or the service—these requirements might change over time and so should be decoupled from the implementation of the service and clients. We should design services so that their policy requirements are independent of any implementation, and we should enforce clients to abide by any policies required by the service. Additionally, all services and client applications must agree on how to specify this policy information (typically by using some sort of configuration file). This is the purpose of the WS-Policy framework, published by the World Wide Web Consortium, and widely adopted by Web service developers.