In his comment on last night's post, Jon asks “do you really believe that Relax NG is the right answer?“
I believe that RNG is a much better answer than XSD. It's simpler, easier to learn and more expressive for many common scenarios (e.g., interleaving). And -- and this is very important -- it's available and ready to use today. At the language level, it can be used with WSDL 1.0 and 2.0 today. Tool support is the issue.
As long as I'm sharing my beliefs, let me continue...
If you know the System.Xml and ASMX/WSE Web service APIs well, you can use them directly to build and send messages that do amazing things.
Most developers don't like to work at that level, they want to simply declare their message structures and message exchange patterns using XSD and WSDL. Most big Web service efforts that span more than one project in an organization end up going here and discover how both complex and inflexible these languages.
Some developers don't even like to work at that level, they want to simply write some C# or VB.NET or Java code and have their XSD and WSDL generated for them. This is certainly the case in early Web service projects.
So most developers want to be at least one and sometimes two layers removed from what is actually going on. The abstraction that most Web service toolkits present to facilitate this is the method call. In fact, it's the same method call on both the client and the service side. This is true whether you start from XSD/WSDL or start from code. In short, for most people today, Web services (even doc/literal ones) are RPC. Many developers like that, because they can deal with objects and methods, and it just works.
But RPC has well-known limitations and the all the people who tried to build big RPC, DCOM, CORBA, Java RMI, and .NET Remoting systems know what they are. The biggest issue I see is around versioning and evolution. The RPC solutions of the past pretty much required simultaneous deployment of clients and servers, making updates to large installations hard.
To my mind, the whole point of loosely-coupled services is to allow us to deploy clients and services independently without breaking everything. This idea is captured in the two of the SOA tenets: boundaries should be explicit and we should focus on contracts not types. It's ironic that, today at least, the tools and infrastructure we use focuses on hiding boundaries and contracts by making them look just like method calls that use our native types. That doesn't mean you can't use today's tools to build to those tenets, but it does mean you have to use them in particular ways (that was the point of my posts on doc/literal/bare, which is the style I use).
So, where am I going with all this? To my first public formulation of what I believe I need in a Web service stack. To wit:
- A focus on messages, not portTypes/operations (interface/methods). Messages carry semantics in the WS stack, which is why WS-Eventing, WS-MetadataExchange, etc. are written in terms of message formats, not portType/operations (there are provided too, but in a non-normative appendix). It doesn't matter what portType a service implements or a client invokes - if they use the same message exchange patterns, they can communicate.
- A simple, tractable language for describing the shape of messages that can be used for both validation and code gen. Given where we are today, I believe it should be RelaxNG. (I can live with XSD if I have to, but I shouldn't have to.)
- A simple, tractable language for describing a service's message exchange patterns. I suppose it could be WSDL, which I know feels hideous, but which is actually much simpler than XSD, especially since most of it is boilerplate. (In fact, you can generate it from a much simpler language).
- A receiver API that dispatches inbound messages to methods for my convenience. A sender API with one generic method that simply takes a message and an action URI and transmits them to an endpoint. WSE's messaging layer provides both of these today. You can write a SoapService (or your own SoapReceiver) that maps inbound messages to methods (or whatever). You can use SoapClient (or SoapSender) directly to send messages. The important thing is that the client and service don't use the same method with the same signature, which is where you fall into RPC. Rather, I want a model similar to event driven programming the way it was back in the day on Windows (and lots of other platforms), where my client posts (request-only) or sends (request-response) messages to a service (which may map them to methods for convenience) which responds.
I like this model because I can build dynamic clients that interrogate a service to know what messages it accepts (thank you WS-MetadataExchange) and then know which messages I can send and what I can expect to receive. This makes versioning and evolution much much easier. A service only has one portType, containing all of its message exchange patterns. An organization can standardize MEPs for use across multiple services. Services can pick and choose the MEPs they want to support. Because the client isn't based on an typed proxy model, you don't have to worry about aligning with service interfaces by type name, just by structure (as with XML). As long as there is some overlap in messages, the client and service can communicate.
You may be thinking that if your client and service agree on portType/binding names, you can do the same sort of negotiation. Yes, that's true. And the benefit is that you can negotiate the presence of a whole series of message exchange patterns just by checking for the presence of a given portType. But as soon as you try to evolve a portType and write a client that knows how to handle portType version mismatches, things complicated. In short, if the service implements all the message exchange patterns you need, but doesn't implement the interface you want, you still can't talk to it. Approaching the problem at the message exchange pattern makes a lot more sense. (For people who remember COM: think IDispatch::GetIdsOfNames, not IUnknown::QueryInterface, but optimized via IProvideClassInfo::GetClassInfo ;-)
You may also be thinking that this model doesn't give you strongly-typed objects to program with. This is not the case, you can take the message definitions and convert them to classes as desired. The client-side API may have a generic method for sending messages, but that doesn't mean the messages can't be modeled as strongly typed objects. Of course, you can still consume your messages as XML if desired.
The good news is that all of the pieces are here now. The bad news is that they aren't mainstream. You can use WSE to get the generic dynamic client model; but with the caveat that ASP.NET Web services are the official path forward to Indigo, so if you do this, expect to port. You can even use RNG if you want to, though you'll have to write your XmlSerializable classes by hand and no one will be able to interop with you.
I try to be optimistic about the RNG change, but XSD has a lot of momentum. Unfortunately, I think it also slows people down and actually limits how far they can go. I guess we'll see.
I've very optimistic about the API model.
We can do a lot better than RPC. If we don't, then what was the point?
Posted
Aug 17 2004, 09:48 AM
by
tim-ewald