Versioning
One of the most critical design decisions you can make is dealing with how to version Web services. As with many other distributed technologies, it's very easy to get this wrong, and hurt yourself in the future. This section deals with some of the major versioning-related design decisions to keep in mind when creating the first version of your Web service, and later versions as well.
Types of Versioning
To begin with, what does versioning mean? There are actually several types of possible versioning schemes:
Typically, interface versioning gets the most attention, but it would be a mistake to not consider the other two types. Each has its own implications. More important, SOAP and the Web service architecture allow us to version each of these in a simple manner. It's a beautiful thing when XML's open content model lets you work such magic.
Loosely Coupled Architecture and Implementation Versioning
The most powerful feature in Web services is not interoperability between heterogeneous environments. It is a valuable feature, and the primary reason why most people have migrated toward it. But the more fundamental reason to use Web services is loose coupling. This is a loaded term that means different things to different people. When I refer to loose coupling, I mean that the actual implementation code for the client or server doesn't directly influence the wire format.
Imagine how powerful this is, and how different from previous distributed technologies such as DCOM. Whereas before, the signature of an implementation would have a direct impact on the wire format, now you can completely change your implementation signatures, technologies, or platforms without changing the wire format. The advantage of this is obvious when you consider interoperability, but it also has the benefit of lowering development costs.
Imagine the following scenario: You build a Web service for accepting purchase orders from an important customer. You design it to be document/literal style, and you use the SOAP Toolkit 2.0. Your shop is full of Visual BASIC 6 (VB 6) developers, so this is a natural choice. Over time, however, you may find that you've migrated to a managed code (C# and .NET Framework) development shop. At some point, you will probably find that maintaining the VB 6 code base for your purchasing service isn't cost effective: It's too much time and wasted effort. Luckily, you can move the service to a .NET code base without breaking the wire format that your partners are expecting. This is a large advantage over distributed technologies of the past.
Even within a single platform such as .NET, you may want to change your implementation radically, for reasons ranging from developer fancy, to security, to performance. As a small example, imagine you have a Web method that looks like this:
[WebMethod]
public string SubmitPO( PurchaseOrder po)
{
return "Order " + po.Id + " received";
}
The PurchaseOrder class may look like this:
public class PurchaseOrder
{
public String Id;
}
The SOAP for the request message would look like this:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<SubmitPO xmlns="http://tempuri.org/">
<po>
<Id>string</Id>
</po>
</SubmitPO>
</soap:Body>
</soap:Envelope>
You can change the signature of the method:
[WebMethod]
[SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Bare)]
public string SubmitPO2( SubmitPurchaseOrder SubmitPO)
{
return "Order " + SubmitPO.po.Id + " received";
}
And the request message stays the same. The response message is different, but the code to keep that the same is just as easy. What's important is that this isn't a .NET feature. This is a feature of Web services. The implementation doesn't dictate the wire format. The implementation of a service or a service client is loosely coupled to the SOAP message that flows over the wire.
Namespaces and Versioning
Put simply, when you version a Web service interface or a data type, you should create a new namespace URI. In fact, all namespace URIs should contain a date, to allow for quick reading to determine which version of a type or interface is being used. For example, instead of the useless http://tempuri.org namespace in our previous example, we could give it a specific namespace with the [WebService] attribute:
[WebService(Namespace="http://foo.com/2002/04/POService")]
public class PurchaseOrderService
The SOAP request also would change, to the following:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<SubmitPO xmlns="http://foo.com/2002/04/POService">
<po>
<Id>string</Id>
</po>
</SubmitPO>
</soap:Body>
</soap:Envelope>
Now, if we decide to create a new service that takes a purchase order, but also takes another SubmitterID, then we should version our interface to be a new URI. There are many ways to do this in .NET. We'll use the same attribute again:
[WebService(Namespace="http://foo.com/2003/08/POService")]
public class PurchaseOrderService
And the SOAP message changes as well:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<SubmitPO xmlns="http://foo.com/2003/08/POService">
<po>
<Id>string</Id>
</po>
</SubmitPO>
</soap:Body>
</soap:Envelope>
You could also imagine versioning the PurchaseOrder type itself. In anticipation of that, you could start by giving it a specific URI:
[XmlRoot(Namespace="http://foo.com/2002/4/PurchaseOrder")]
public class PurchaseOrder
{
public String Id;
}
This would create a wire format as follows:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<SubmitPO xmlns="http://foo.com/2002/04/PO">
<po>
<Id xmlns="http://foo.com/2002/4/PurchaseOrder">string</Id>
</po>
</SubmitPO>
</soap:Body>
</soap:Envelope>
I would recommend that you always version your data types along with your interfaces. With .NET, this would eliminate the need to put an explicit namespace on each type, because instead you could put it just at the Web method or Web service level. (With .NET, you can use the [XmlRoot] attribute to set namespaces on classes. This would be the easiest way to version.)
|