У нас есть 2 отдельных продукта, которые должны общаться друг с другом через веб-сервисы. Что лучшая практика должна поддержать versioining API?
У меня есть эта статья с 2004, утверждая, что нет никакого фактического стандарта и только лучших практик. Какие-либо лучшие решения? Как Вы решаете управление версиями WS?
class SystemAClient{
SystemBServiceStub systemB;
public void consumeFromB(){
SystemBObject bObject = systemB.getSomethingFromB(new SomethingFromBRequest("someKey"));
}
}
class SystemAService{
public SystemAObject getSomethingFromA(SomethingFromARequest req){
return new SystemAObjectFactory.getObject(req);
}
}
Версия 1
class SystemAObject{
Integer id;
String name;
... // getters and setters etc;
}
Версия 2
class SystemAObject{
Long id;
String name;
String description;
... // getters and setters etc;
}
Версия 1
class SomethingFromARequest {
Integer requestedId;
... // getters and setters etc;
}
Версия 2
class SomethingFromARequest {
Long requestedId;
... // getters and setters etc;
}
class SystemBClient{
SystemAServiceStub systemA;
public void consumeFromA(){
SystemAObject aObject = systemA.getSomethingFromA(new SomethingFromARequest(1));
aObject.getDescription() // fail point
// do something with it...
}
}
class SystemBService{
public SystemBObject getSomethingFromB(SomethingFromBRequest req){
return new SystemBObjectFactory.getObject(req);
}
}
Версия 1
class SystemBObject{
String key;
Integer year;
Integer month;
Integer day;
... // getters and setters etc;
}
Версия 2
class SystemBObject{
String key;
BDate date;
... // getters and setters etc;
}
class BDate{
Integer year;
Integer month;
Integer day;
... // getters and setters etc;
}
Версия 1
class SomethingFromBRequest {
String key;
... // getters and setters etc;
}
Версия 2
class SomethingFromBRequest {
String key;
BDate afterDate;
BDate beforeDate;
... // getters and setters etc;
}
Если Система, клиент версии 1 называет Систему B обслуживанием версии 2, на которой это может потерпеть неудачу:
SystemBObject
(getYear()
, getMonth()
, getDay()
)BDate
Если Система, клиент версии 2 называет Систему B обслуживанием версии 1, на которой это может потерпеть неудачу:
BDate
на SomethingFromBRequest
(Клиент использует более новый объект запроса B, который версия 1 B не признает),SystemBObject
объект (getDate()
)Если Система B клиент версии 1 называет Систему обслуживанием версии 2, это может потерпеть неудачу на:
SystemAObject
(возвращенный Long
но ожидаемый Integer
)Если Система B клиент версии 2 называет Систему обслуживанием версии 1, это может потерпеть неудачу на:
SystemARequest
(запрос Long
вместо Integer
)Long
но обслуживание возвращается Integer
не nessecarily совместимый во всех внедрениях WS)SystemAObject1
, SystemBRequest2
и т.д., но это пропускает API для соответствия источнику / целевая версия Я предпочитаю метод SalesForce.com версию. Каждая версия веб-сервисов получает отчетливый URL в формате:
http://api.salesforce.com/{version}/{serviceName}
, поэтому у вас будет URL-адреса веб-сервисов, похожее на:
http://api.salesforce.com/14/Lead
http://api.salesforce.com/15/Lead
и так далее ...
с этим методом, вы получаете преимущества из:
Вы всегда знаете, с какой версией вы разговариваете.
Обратная совместимость поддерживается.
Вам не нужно беспокоиться о проблемах на зависимости. Каждая версия имеет полный набор услуг. Вам просто нужно убедиться, что вы не смешиваете версии между звонками (но это до потребителя сервиса, а не как разработчик).
Решение состоит в том, чтобы избежать несовместимых изменений ваших типов.
Возьмем, к примеру, SystemBObject. Вы описываете «версию 1» и «версию 2» этого типа, но это совсем не один и тот же тип. Совместимое изменение этого типа включает только Добавление свойств, а не изменение типа каких-либо существующих свойств. Ваше гипотетическое «обновление версии» нарушило оба этих ограничения.
Следуя этому единственному правилу, вы можете избежать ВСЕХ проблем, которые вы описали.
Следовательно, если это определение вашего типа в версии 1
class SystemBObject{ // version 1
String key;
Integer year;
Integer month;
Integer day;
... // getters and setters etc;
}
Тогда это не может быть определение вашего типа в версии 2:
// version 2 - NO NO NO
class SystemBObject{
String key;
BDate date;
... // getters and setters etc;
}
... потому что оно удалило существующие поля.Если это изменение, которое вам необходимо сделать, это не новая «версия», это новый тип, и он должен называться соответствующим образом как в коде, так и в формате сериализации.
Другой пример: Если это ваш существующий тип v1:
class SomethingFromARequest {
Integer requestedId;
... // getters and setters etc;
}
... тогда это недопустимый "v2" этого типа:
class SomethingFromARequest {
Long requestedId;
... // getters and setters etc;
}
... потому что вы изменили тип существующего свойства.
Эти ограничения более подробно объясняются в основном технологически нейтральным способом в статье Microsoft Service Versioning .
Помимо исключения этого источника несовместимости, вы можете и должны включать номер версии в тип. Это может быть простой серийный номер. Если у вас есть привычка регистрировать или проверять сообщения, а пропускная способность и пространство для хранения не являются проблемой, вы можете захотеть дополнить простое целое число UUID, чтобы идентифицировать экземпляр каждой уникальной версии типа.
Кроме того, вы можете разработать прямую совместимость с вашими объектами передачи данных, используя слабую обработку и отображая «лишние» данные в «дополнительное» поле. Если XML является вашим форматом сериализации, вы можете использовать xsd: xmlAny или xsd: any и processContents = "lax" для захвата любых нераспознанных элементов схемы, когда служба v1 получает запрос v2 ( подробнее ). Если ваш формат сериализации - JSON с его более открытой моделью содержимого, то это предоставляется бесплатно.
Я знаю, что это поздно для игры, но я достаточно глубоко вник в эту проблему. Я действительно думаю, что лучший ответ включает в себя еще один кусок головоломки: сервисный посредник. Microsoft Managed Services Engine является примером одного - я уверен, что существуют и другие. По сути, изменяя пространство имен XML вашего веб-сервиса (чтобы включить номер версии или дату, как упоминается в связанной статье), вы предоставляете посреднику возможность направлять различные клиентские вызовы к соответствующим реализациям сервера. Дополнительная (и, IMHO, очень крутая) функция MSE - это возможность выполнять преобразование на основе политик. Вы можете определить преобразования XSLT, которые преобразуют запросы v1 в запросы v2 и ответы v2 в ответы v1, что позволяет отказаться от реализации служб v1 без нарушения клиентских реализаций.
Я думаю, что еще нужно иметь в виду вашу клиентскую базу, публикуете ли вы эту услугу публично или она ограничена набором известных агентов?
Я участвую в последней ситуации, и мы обнаружил, что решить эту проблему с помощью простого общения / участия не так уж и сложно.
Хотя это только косвенно связано с вашим вопросом, мы обнаружили, что основание нашего номера версии на совместимости, похоже, работает достаточно хорошо. На примере ABC ...