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
BDate
on 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
SystemBObject
object (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
(returnedLong
but 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
(requestLong
instead ofInteger
) - If the request passed somehow, casting issues (the stub is
Long
but the service returns anInteger
not nessecarily compatible in all WS implementations)
Possible solutions
- Use numbers when advancing versions: e.g.
SystemAObject1
,SystemBRequest2
etc 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?