Why I want WSDL to die.

Frequent readers of this blog, people who've seen my Web Services demos and those who downloaded the source for my ASP.NET SoapExtensions (yet another link) know that I am pretty serious about supporting the WSDL features that are there.

Whenever you plug a security feature or a transaction feature or a session feature into the ASP.NET pipeline using one of my SoapExtension attributes, I will make sure that the WSDL gets properly augmented, and that all the schemas and headers get emitted in the right places (which most SoapExtensions don't do because the documentation is suboptimal, at best). I will also make sure that the client portion will evaluate the format extensions emitted into WSDL and code-generate the necessary client extensions by hooking into VS.NET or wsdl.exe. The goal is that the effort to implement the services part of an end-to-end solution that is secure, promotes TIP transactions and is session aware will be close to zero. I strongly believe in "ease of use" there - even complex (read: powerful and serious) web services should not need to be complicated to build. I am using the expressiveness of WSDL to carry as much metadata as I can to make that work. The goal of my extensions is to make and carry that statement and provide an example of how to do implement it.

So, I hereby declare myself as an expert on that matter ;)  Based on what I've seen WSDL do for me and looking at what's been evolving in the WS-* spec realm lately, I want it to die. I think its time is up and we no longer need it and I believe it will cause more problems than provide solutions moving forward. Here are a few questions illustrating why:

  • Show me a good reason for why I would want to have a <wsdl:message> element that contains more than a single reference to an XSD element definition. Examples of <wsdl:message> with multiple elements sidestep <xsd:complexType>. If 1:1 mappings from <wsdl:input> to <wsdl:message> and <wsdl:message> to schema is what's left to keep things conherent, why have it? (I respect that there are other schema languages, read on)
  • Show me a good reason for why I would want to maintain a flat "interface" notion where I have a fixed set of operations bound to a single endpoint. Either an endpoint "accepts more" as per Sam Ruby's mantra or an endpoint accepts a single well-defined thing. The "interface" idea is a remainder of the thinking of IDL compilers that had to emit a compilable chunk of code. A web service endpoint should be either a "little yellow thing" (or for the US: a "little blue thing") that acts like a mailbox and happily accepts any letter and deals with its out-of-band metadata (a router or dispatcher would do that) or it is something that understands a single semantically well understood message that may be expressed based one of multiple supported schema variants (--> REST folks, here you go.). "Interfaces" in a web services world must be dynamic, regroupable "business process endpoint" definitions that group all endpoints that are part of a message exchange pattern. One endpoint can be part of many business processes. The "interface" notion doesn't work on that level. It's a code-driven thing, not a service- and data-driven thing.
  • Show me a good reason for why I would want to put a single header-reference into an operation binding if most protocol extensions (WS-Security) cry out loud for a flexible policy mechanism as defined by WS-Policy, anyways. If I decide that the transport for my SOAP package is going to be a message queue, I probably don't want to do message-level reliable messaging control, because that'd mean overhead and I sure need it for HTTP. How do I express such choices in WSDL where the "required"ness of a header depends on the underlying transport? I can't. It's going to be wrong either way. If I rely fully on WS-Policy and don't declare my headers, the WSDL is invalid.
  • Show me a good way to express <wsdl:service> in the presence of WS-Routing. In short, you can't. At the time of (early) binding, you get an endpoint reference that may go away the next minute, while your next routing hop may very well know where it went. And how do I bind to a service that I can't see but which I only can route to? Will every router have to be able to proxy and provide an adjusted version of any WSDL that lurks behind it so that the <wsdl:service> binding is accurate?
  • Speaking of routers; in the presence of WS-Routing all <wsdl:operation> header information will be void once you get switched over to an intermediary that requires additional out-of-band information such as authentication in its role. You can request a router's WS-Policy dynamically and act on it, but do you really want to dynamically rebind to an intermediary's mod'ded version of a foreign WSDL? Why have two dynamic things to look at?

What's left then? This:

<operation name="ReserveSeat">
  
<input message="res:ReserveSeatInput" /> 
   <output message="res:ReserveSeatInput" /> 
</operation>

which could easily be expressed by augmenting XSD properly:

<?xml version="1.0" encoding="utf-8" ?>
<
s:schema xmlns:s="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
              
xmlns="urn:schemas-newtelligence-com:flightsrus:demo:v4"
              
xmlns:operations="urn:schemas-newtelligence-com:messaging:metadata"
              
targetNamespace="urn:schemas-newtelligence-com:flightsrus:demo:v4">
  
<s:element operations:operation="ReserveSeat" operations:direction="input" name="ReserveSeatInput">
     
<s:complexType>
        
<s:sequence>
           
<s:element minOccurs="0" maxOccurs="1" name="Airline" type="s:string" />
           
<s:element minOccurs="0" maxOccurs="1" name="FlightNumber" type="s:string" />
           
<s:element minOccurs="1" maxOccurs="1" name="Row" type="s:int" />
           
<s:element minOccurs="0" maxOccurs="1" name="Seat" type="s:string" />
           
<s:element minOccurs="0" maxOccurs="1" name="DepartureAirport" type="s:string" />
           
<s:element minOccurs="1" maxOccurs="1" name="DepartureTime" type="s:dateTime" />
           
<s:element minOccurs="0" maxOccurs="1" name="ArrivalAirport" type="s:string" />
       
</s:sequence>
   
</s:complexType>
 
</s:element>
  <
s:element operations:operation="ReserveSeat" operations:direction="output" name="ReserveSeatOutput">
   
<s:complexType>
      
<s:sequence>
         
<s:element minOccurs="0" maxOccurs="1" name="ReserveSeatResult" type="s:string" />
     
</s:sequence>
  
</s:complexType>
 
</s:element>
 
<s:element operations:operation="ReserveSeatVariant1" operations:direction="input"
                 
name="ReserveSeatVariant1Input" ref="ReserveSeatInput" />
</
s:schema>

In the end, all I want is to know is a set of augmented schemas that an endpoint understands and can make sense of, some metadata to map that properly into a client-side and server-side (sum that up as "endpoint side") programming model and information about the policies that the endpoint and any intermediary on the route want to enforce and that I need to respect. XSD and about any other schema format will allow the former, WS-Policy takes care of the latter. WS-Routing (and what it'll turn into very soon) will take care of finding and dispatching to the appropriate service endpoint. Dump WSDL.

(Hint: From such annotated schema, WSDL is just an XSLT transform away in case you're worried about backwards compatibility)

Updated: