I’ve been working with two web service APIs recently and pondering issues. The first issue is what I’ve been calling the “magic string” parameter. In both APIs I’ve had to dig through the documentation to find all the legal values for a “magic string” parameter. For instance:
string mode = “verbose”; service.GetData(id, mode);
string mode=”concise”; service.GetData(id, mode);
Magic strings are nothing new in the web service world, but in the APIs I’ve been working with the documentation hasn’t been helpful in identifying all the possibilities. How do I know “verbose” and “concise” are the only two options? Perhaps I’m just spoiled by intelli-sense and enumerations. When I need to pass a FileMode parameter to File.Open, the six available options are just a pick list away.
What bothers me is how primitive the approach feels. We’ve mostly abstracted away memory management, made web forms feel like windows forms, and write software 8 layers above the hardware. Still, I’m passing a carefully ordered collection of characters to an API in the name of interoperability.
The next step is to abstract away the primitiveness. Bury all these magic invocations inside of a framework and allow the application developer to invoke instance methods with enumerations. But wait, I think, soon the application developer will be willy nilly calling methods which involve network hops across trust boundaries and have no idea of the turmoil occurring underneath this thin layer. The boundaries, as the SO tenets say, should be explicit.
Many of the essays on explicit boundaries concentrate on the server side of the design. For instance, Clemens Vasters: “Forbidding foreign access or even knowledge about service internals allows radical changes inside and “underneath” services”.
Hiding service details is one-half of the design. I found a patterns and document addressing the second half: “The idea that you can take a local object interface and extend it across machine boundaries to create location transparency is flawed.”, and later: “From the client perspective, a remote implementation of the interface is subject to network latency, network failure, and distributed system failures, but a local implementation is not. A significant amount of error detection and correction logic must be written to anticipate the impacts of using remote object interfaces.“.
So the next question is: how do we make the boundary explicit in client code? How do we let the application programmer know that the code they just typed in might not be a stroll through the neighborhood app domain, but might just be a 747 trip across the oceans and back, complete with custom declarations and long lines at the security checkpoints?
In looking for more thoughts about the matter, I found a lot of theory. Ingo Rammer, however, addressed the topic head on with an example using an interface (IRemoteCustomerManager) and a helper class (RemotingHelper). As Ingo says: “Even when looking at this piece of code months after you’ve written it, it will be immediately obvious: you are accessing a remote component.”
So I’m following a similar pattern and I feel better about the design, but I now worry how the code will age. Methods and code are constantly refactored by developers. Eventually, someone will need to aggregate data from two services and end up pushing everything into another layer. Somehow, somewhere, a boundary will become implicit. Of all the SOA tenets, I think this one will be the toughest for architects to enforce.