The
Holy Roman Empire was, as Voltaire famously remarked, "neither Holy, nor Roman, nor an Empire." Likewise, the [DataContract] in Indigo is neither data nor a contract. Moreover, like the putative Empire, the [DataContract] will never reach its rather ambitious goals, due to the inherent contradictions of its conflicted nature. Just as the German kings who styled themselves emperors initially used the imprimatur of the Papal crowning to enhance their power and prestige but eventually were irreversibly weakened by that dependence, the [DataContract] gains a great deal of apparent power from its connection to your domain object model, but its dependence on your internal object model ultimately undermines its utility.
Not a Contract
A contract is "an agreement between two or more parties." In most discussions of services, the term contract is used in an entirely inappropriate fashion. Because the consuming side of the "contract" is unknown at the time the "contract" is published, we should hardly be calling this thing a contract. When you specify the [DataContract] in an Indigo app, you are stating both an expectation and a commitment. Your expectation is that consumers of your service will send you the specific type of message you are describing and your commitment is that, if the message meets your specification, you will do something with it. Most (but not all) of the versioning problems that contract-based distributed computing stacks have are a direct result of mistaking this expectation and commitment for a contract. If we want to build real-world business systems effectively, we need to be able more flexibility than a contract allows. For example, we need be able to specify multiple (potentially unrelated) message types with the same endpoint and operation. Rather than a system that, like both Indigo and ASMX, completely hides the transformation between the messages and our internal processing model, we need a system that surfaces that transformation to the developer. The transformation between the external messages and internal processing is a key part of the business logic of any service. Having to control that transformation indirectly through attributes is slow, frustrating, and error-prone.
Not Data
The problem with thinking of messages as data is that data has no meaning on its own. One of the differences between RPC and message-oriented programming is that messages, unlike the parameters of an RPC, can have meaning outside the particular service endpoint to they are addressed to. The [DataContract]/[DataMember] paradigm works fine if your understanding of what you're doing is mapping individual parameters of a remote call to the state of an object, but it breaks down quickly if you want to actually design a message with rich semantics. It is certainly possible to achieve a message with rich semantics using the paradigm, but the attribute-based approach is clumsy at best. The important semantics of the message are controlled by the relationship of the attributes to the class members.
Not the Best Way to Design Messages
Designing the messages that your services process is a key part of building a service oriented architecture. In the Indigo model, the preferred way to do this is to design a class (that is part of your internal processing model) and annotate that class with the appropriate attributes. This seems to be part of a deliberate strategy to hide the message from the developer. As an Indigo developer using the [DataContract] mechanism, your primary view of the message is a fragmented set of attributes attached to a class. Your secondary view is a confusing XML Schema that's cluttered with detritus needed only to support the Indigo infrastructure. In neither case is it easy to tease out what a instance of your message would actually look like. There's a very clear assumption that the XML Schema that is generated is only going to be consumed by toolsets similar to Indigo. These schemas should have annotation stating "Not Intended for Human Consumption." Unfortunately, the sheer complexity of supporting all of the [DataContract] magic makes it very difficult for alternate toolsets to productively consume these schemas (or schemata, if you prefer). This will limit interoperability for developers going down that route.
If Not [DataContract], Then What?
Of course, you always have the option of designing an XML Schema in some alternative tool and generating a .Net class from it. Unfortunately, Indigo doesn't seem to have advanced the state of the art very much here. That's more due to the inherent complexity of XML Schema than any Indigo deficiency. Starting from an XML Schema design leads one down a path of creating message types that are difficult, if not impossible, to consume in .Net (dealing with dates is but one obvious example). Beyond that, XML Schema is, in many respects, ill-suited for message design. First, and foremost, schema validation is all or nothing. While for some scenarios that is a good thing, for many scenarios all or nothing validation is inappropriate. For example, there is no easy way to design a schema that will ignore an optional element with the wrong datatype (as far as I know, I'm never quite sure about anything with XML Schema).
Is [DataContract] Evil?
Not at all. In fact, if you have an existing object model that you want to expose to an Indigo service, it is a great tool. I just don't think it is the best way to design new services because it directly couples your service's messages to your internal processing model via an enormous hidden infrastructure that you can affect only by cumbersome, trial and error attribute twiddling. In spite of that, I expect it will be fairly successful. Just as the Holy Roman Empire lasted for the better part of a millennium in spite of its rather obvious flaws, [DataContract] will be the basis for a generation of distributed applications.
Posted
Jun 04 2005, 12:13 PM
by
john-cavnar-johnson