We have 2 separate products that need to communicate with each other via web services.
What is the best-practice to support versioining of the API?
I have this article from 2004 claiming there is no actual standard, and only best practices. Any better solutions? How do you solve WS versioning?
Problem Description
System A
Client
class SystemAClient{
SystemBServiceStub systemB;
public void consumeFromB(){
SystemBObject bObject = systemB.getSomethingFromB(new SomethingFromBRequest("someKey"));
}
}
Service
class SystemAService{
public SystemAObject getSomethingFromA(SomethingFromARequest req){
return new SystemAObjectFactory.getObject(req);
}
}
Transferable Object
Version 1
class SystemAObject{
Integer id;
String name;
... // getters and setters etc;
}
Version 2
class SystemAObject{
Long id;
String name;
String description;
... // getters and setters etc;
}
Request Object
Version 1
class SomethingFromARequest {
Integer requestedId;
... // getters and setters etc;
}
Version 2
class SomethingFromARequest {
Long requestedId;
... // getters and setters etc;
}
System B
Client
class SystemBClient{
SystemAServiceStub systemA;
public void consumeFromA(){
SystemAObject aObject = systemA.getSomethingFromA(new SomethingFromARequest(1));
aObject.getDescription() // fail point
// do something with it...
}
}
Service
class SystemBService{
public SystemBObject getSomethingFromB(SomethingFromBRequest req){
return new SystemBObjectFactory.getObject(req);
}
}
Transferable Object
Version 1
class SystemBObject{
String key;
Integer year;
Integer month;
Integer day;
... // getters and setters etc;
}
Version 2
class SystemBObject{
String key;
BDate date;
... // getters and setters etc;
}
class BDate{
Integer year;
Integer month;
Integer day;
... // getters and setters etc;
}
Request Object
Version 1
class SomethingFromBRequest {
String key;
... // getters and setters etc;
}
Version 2
class SomethingFromBRequest {
String key;
BDate afterDate;
BDate beforeDate;
... // getters and setters etc;
}
Fail Scenarios
If a System A client of version 1 calls a System B service of version 2 it can fail on:
- missing methods on
SystemBObject(getYear(),getMonth(),getDay()) - Unknown type
BDate
If a System A client of version 2 calls a System B service of version 1 it can fail on:
- Unknown type
BDateon theSomethingFromBRequest(A client uses a newer B request object that B version 1 doesn’t recognize) - If the System A client is smart enough to use version 1 of the request object, it can fail on missing methods on the
SystemBObjectobject (getDate())
If a System B client of version 1 calls a System A service of version 2 it can fail on:
- Type missmatch or overflow on
SystemAObject(returnedLongbut expectedInteger)
If a System B client of version 2 calls a System A service of version 1 it can fail on:
- Type missmatch or overflow on
SystemARequest(requestLonginstead ofInteger) - If the request passed somehow, casting issues (the stub is
Longbut the service returns anIntegernot nessecarily compatible in all WS implementations)
Possible solutions
- Use numbers when advancing versions: e.g.
SystemAObject1,SystemBRequest2etc but this is missing a an API for matching source / target version - In the signature, pass XML and not objects (yuck, pass escaped XML in XML, double serialization, deserialization / parsing, unparsing)
- Other: e.g. does Document/literal / WS-I has a remedy?
I prefer the Salesforce.com method of versioning. Each version of the Web Services gets a distinct URL in the format of:
So you’ll have Web Service URLs that look like:
and so on…
With this method, you get the benefits of:
You always know which version you’re talking to.
Backwards compatibility is maintained.
You don’t have to worry about dependency issues. Each version has the complete set of services. You just have to make sure you don’t mix versions between calls (but that’s up to the consumer of the service, not you as the developer).