views:

182

answers:

3

I have a SOAP service that exposes a method

TradeDetail getTradeDetail()

TradeDetail stores 5 fields, transaction number, dates etc

I need to add a couple of fields to TradeDetail. I want to keep backward compatibility (for a while) and it looks as if my options are limited to creating a new class with the extra fields

TradeDetail2 getTradeDetail2()

Now this will work - I've done it before. But are there any other solutions that people have used?

E.g.

  • Fundamentally change TradeDetail2 to add name value pairs.
  • Inherit TradeDetail2 from TradeDetail, this would reduce code but increase coupling
  • Return XML or JSON instead

I will be able to retire the original interface pretty quickly so the code will get cleaned up and the extra TradeDetail2 won't last forever!

thanks

+2  A: 

I sympathise - some of my webservices are riddled with myMethod(), myMethod2(), myMethod3() etc simply because I needed to add a few new fields.

Would it make sense for you to keep the method name and create a new endpoint for each version of your API instead? eg:

Then your method names stay sensible, regardless of how many future changes you need to make.

Any apps using your webservice would probably need to be rewritten and/or rebuilt against a new WSDL anyway in order to take advantage of the new fields, so why not just have them rewritten/rebuilt against the new v1.1 API.

I find that this also helps when communicating with the owners/developers of the apps using your service - eg, "Version [old] of our webservice API will no longer be supported after [date], please ensure that you are using at least version [new]."

LukeH
+1  A: 

This article Best practices for Web services versioning may be of some use.

nzpcmad
+1  A: 

This is why I prefer to have complete control over XML to Object mapping, so that I can separate model from XML interface. In your case, I would simply add new fields to TradeDetail, and consider them "optional" for backwards compatibility. This would be the example XML->Object mapping for TradeDetail in framework my team uses, written for your interface:

// this would go into my client endpoint class
public TradeDetail getTradeDetail() {
  Element requestRoot = new Element("GetTradeDetail");
  Element responseRoot = invokeWebServiceAndReturnJdomElement(requestRoot);
  return mapTradeDetail(responseRoot);
}

// this would go into my client XO mapping class
public TradeDetail mapTradeDetail(Element root) {
  TradeDetail tradeDetail = new TradeDetail();
  tradeDetail.setField1 = fetchString(root, "/GetTradeDetail/Field1");
  tradeDetail.setField2 = fetchInteger(root, "/GetTradeDetail/Field2");
  tradeDetail.setField3 = mapField3(root, "/GetTradeDetail/Field3");
  tradeDetail.setField4 = fetchString(root, "/GetTradeDetail/Field4");
}

This kind of client would ignore new fields, thus being compatible with new version of protocol, until I add something like this to the end of this same method in version 2:

if (fetchXPath(root, "/GetTradeDetail/Field5") != null) {
  // so we're talking with server which speaks new version of protocol
  tradeDetail.setField5 = fetchString(root, "/GetTradeDetail/Field5");
}

Server would work with similar code, possibly checking client version, and mapping extra fields only if client supports new version of protocol.

In my view, client should be written so that few extra fields added to protocol don't break the client - I don't have the luxury of being down simply because upstream provider added new functionality and didn't inform me about it. If provider changes existing mandatory fields, of course, client needs modification. This is why upstream provider should version protocol and support old version for at least a couple of months.

Domchi