Contract-First or Code-First Design - Part 4

Editor’s note: This is the fourth of a multi-part series of transcriptions of a conversation between Adrian Trenaman and Ted Neward on Web services in general and code-first/contract-first practices in particular, secretly recorded by an undercover agent of TheServerSide. The conversation has just moved to Adrian’s third reason for preferring contract-first and WSDL.)

Adrian: OK, third reason: Generated WSDL leads to interface bloat and duplicated and incompatible type definitions. The Address example alluded to the fact that WSDL generation tools generate not only the WSDL interface, but also supporting XSD data-types for other classes used by the interface. Typically these XSD types are lumped into the WSDL contract in the section. Now, consider what happens when you generate an interface from the following:

(Editor’s note: Again, our agent–cleverly disguised as track lighting–was able to capture this example scribbled on the cocktail napkin.)

class CustomerInformation {
void newCustomer(Customer c) {
}
};
… where the Customer class refers to the Address class noted above. The generated WSDL contract for the CustomerInformation service will contain XSD for the Address data type; this is a needless duplication of a similar XSD defined in the AddressBook WSDL. But it’s not the duplication the worries me particularly - I can live with that.

Ted: Maybe you can, but I always worry about duplication; it’s a violation of Dave [Thomas]’s DRY principle.

Adrian: DRY?

Ted: “Don’t Repeat Yourself”. Pragmatic Programmer, item twenty-something.

Adrian: Oh, right. Well, duplication is bad, agreed, but what’s more worrying is that this new Address schema will probably be defined in a different namespace from the original. As such, it’s structurally similar to the original but completely incompatible, both on the wire and in code that attempts to use both services. When you design WSDL first, you can take care to design an appropriate data model using XSD. Types from this data model can then be reused via the tag in numerous WSDL contracts, and, because you’re always referring to exactly the same type, then you don’t have problems with interoperability.

Ted: I think you’re assuming that WSDL design is going to somehow enforce good design in general, though, and I think that’s a false pretense. Just because a developer is designing something in WSDL doesn’t mean they’re going to design a WSDL that will reuse existing XSD definitions correctly or accurately; in fact, I’d argue that too many Web service developers out there don’t even know about the tag whatsoever, or the relationship of to the namespaces in the WSDL and XSD documents. But they do know about reuse of code, and if the relationship of types is done in a code-based fashion, using mechanisms that they recognize and understand, like jar files or .NET assemblies, then they have a better chance of reusing types across services.

But even that is a concern to me: I don’t necessarily want developers trying to reuse types across services, because again we’re back to the dangers of tight coupling and its “ripple effect” when a change occurs. Suppose a developer does what you’re advocating, and successfully s the Customer schema definition across three WSDL documents. Now, for the fourth service, we have to change the definition of Customer to include information that’s not present currently. How’s your feeling about making that change? Exactly! Everybody who uses those three other services suddenly now have to incorporate that change into their system as well, regardless of whether they wanted it or not, because the other three WSDL docs all explicitly that definition. So now we have to play some silly XSD games to try and reuse the Customer definition in this fourth service definition and still be able to extend it somehow, which is likely to break most of the XSD-to-code code-generation tools.

Adrian: So, what, you want four different definitions for Customer?

Ted: Absolutely! Services are supposed to be independent of one another, so that a change to one doesn’t trigger that kind of ripple effect. Reuse is about creating a deliberate coupling between two parts of the code base, and these two goals are largely incompatible to one another. If the goal is really to create loosely- coupled services, we have to be willing to “let go” the reuse goal, particularly across service definitions. What you do on the inside of a service is totally up to the service implementor, so if reuse of implementation is appropriate there, then I’m all for it, but not at the expense of the loose coupling that we all supposedly agree is the goal of a well-built Web service.

Adrian: (Laughter) Heresy!

(Editor’s note: At this point, several video game fans start chanting “He-re-tic! He-re-tic!” in high squeaky voices in the background, and only through the timely intervention of our agent–cleverly disguised as the Master Chief–enabled the conversation to continue.)

Part 3 — Part 5