Шрифт:
Давайте сравним близкий к реальному псевдокод на сторонах клиента в рамках CORBA с псевдокодом в среде веб-служб. При поддержке сессии все достаточно прозрачно и не нуждается в комментариях.
Псевдокод транзакции в среде CORBA
CosTransactions.Current current = CosTransactions.CurrentHelper.Narrow(
orb.ResolveInitialReferences("TransactionCurrent"));
current.Begin;
try
{
store1.Remove(product, quantity);
store2.Append(product, quantity);
current.Commit;
}
catch (Exception e)
{
current.Rollback;
ShowError("Ошибка выполнения операции: " + e.toString);
}В среде веб-служб в программе-клиенте приходится надстраивать абстракции DTO [81] . А в серверном приложении, где используются соединения, например, с СУБД или монитором транзакций, необходимо фактически дублировать предыдущий код с раскруткой объекта – единицы работы (unit of work) в реальную транзакцию.
Псевдокод транзакции в среде веб-служб
StoreServiceClient storeServiceClient = new StoreServiceClient(url);
StoreOperationDTO operation1 = storeServiceClient.CreateOperation(store1.Id);
operation1.Type = StoreOperations.Remove;
operation1.ProductId = product.Id;
operation1.Quantity = quantity;
StoreOperationDTO operation2 = storeServiceClient.CreateOperation(store2.Id);
operation2.Type = StoreOperations.Append;
operation2.ProductId = product.Id;
operation2.Quantity = quantity;UnitOfWork uow = new UnitOfWork;
uof.RegisterDirty(operation1);
uof.RegisterDirty(operation2);
try
{
storeServiceClient.ProcessOperations(uow);
}
catch (Exception e)
{
ShowError("Ошибка выполнения операции: " + e.toString);
}Вторым «упрощением» стал переход от понятных прикладному программисту деклараций интерфейсов объектов и служб на языке IDL [82] к WSDL [83] – описаниям, ориентированным, прежде всего, на обработку компьютером. Сравним декларации складской службы, возвращающей по запросу текущее количество товарных позиций.
Декларация службы в CORBA IDL
module StockServices
{
typedef float CurrentQuantity;
struct QuantityRequest
{
string stockSymbol;
};
interface StockInventoryService
{
CurrentQuantity getCurrent(in QuantityRequest request);
};
};Декларация службы в WSDL
<?xml version ="1.0" encoding ="utf-8"?>
<definitions name ="StockInventoryService"
xmlns: sqs ="http://mycompany.com/stockinventoryservice.wsdl"
xmlns: sqsxsd ="http://mycompany.com/stockinventoryservice.xsd"
xmlns: soap ="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns: wsdl ="http://schemas.xmlsoap.org/wsdl/"
xmlns: xsd ="http://www.w3.org/2000/10/XMLSchema">
<wsdl: types>
<xsd: element name ="CurrentQuantity">
<xsd: complexType>
<xsd: all>
<xsd: element name ="stockSymbol" type ="string"/>
</xsd: all>
</xsd: complexType>
</xsd: element>
<xsd: element name ="CurrentQuantity">
<xsd: complexType>
<xsd: all>
<xsd: element name ="quantity" type ="float"/>
</xsd: all>
</xsd: complexType>
</xsd: element>
</wsdl: types><xsd: element name ="CurrentQuantity">
<xsd: complexType>
<xsd: all>
<xsd: element name ="quantity" type ="float"/>
</xsd: all>
</xsd: complexType>
</xsd: element>
</wsdl: types>
<wsdl: message name ="getCurrentInput">
<wsdl: part name ="body" element ="sqsxsd: CurrentQuantity"/>
</wsdl: message><wsdl: message name ="getCurrentOutput">
<wsdl: part name ="body" element ="sqsxsd: CurrentQuantity"/>
</wsdl: message>
<wsdl: portType name ="StockInventoryServicePortType">
<wsdl: operation name ="getCurrent">
<wsdl: input message ="sqs: getCurrentInput"/>
<wsdl: output message ="sqs: getCurrentOutput"/>
</wsdl: operation>
</wsdl: portType><wsdl: binding name ="StockInventoryServiceSoapBinding"
type ="sqs: StockInventoryServicePortType">
<soap: binding style ="document" transport ="http://schemas.xmlsoap.org/soap/http"/>
<wsdl: operation name ="getCurrent">
<soap: operation soapAction ="http://mycompany.com/getCurrent"/>
<wsdl: input>
<soap: body use ="literal"/>
</wsdl: input>
<wsdl: output>
<soap: body use ="literal"/>
</wsdl: output>
</wsdl: operation>
</wsdl: binding><wsdl: service name ="StockInventoryService">
<wsdl: port name ="StockInventoryServicePort"
binding ="sqs: StockInventoryServiceBinding">
<soap: address location ="http://mycompany.com/StockInventoryService"/>
</wsdl: port>
</wsdl: service>
</definitions>Третьим «усовершенствованием» стал отказ от автоматической подгрузки связанных объектов [84] в пользу исключительно ручного управления процессом.
В CORBA объекты функционируют на сервере, тогда как на клиенте находится только соответствующая заглушка ( stub ). То есть вы в программе вызываете какой-то метод, а на самом деле происходит обращение к серверу, вызов соответствующего метода у серверного объекта и возврат результата на клиента с возможным обновлением состояния локальных полей заглушки. Аналогично со свойствами объектного типа: связанный объект подгружается по мере необходимости. Всё это происходит прозрачно для программиста, которому не нужно вмешиваться в процесс взаимодействия, но желательно знать, что стоит за манипуляциями с заглушкой на клиенте. Соответственно, существует соблазн вместо реализации на сервере новой функции службы написать код непосредственно на клиенте, благо сделать это легко. Тогда, например, обработка достаточно большой коллекции объектов в цикле может вызвать интенсивный обмен сообщениями с сервером и возникновение узкого места в системе. Аналогичная проблема плохой реализации имеется и при работе приложения напрямую с СУБД.
В среде веб-сервисов вопрос с «нерадивым программистом» решили радикально – отменой самой возможности написать такой код. Несмотря на то что в 80 % случаев имевшаяся автоматическая загрузка была уместной и здорово сокращала программу.
Как уберечь кукурузу от насекомых-вредителей? Очень просто: выкосить её всю, к чертям. Вредители придут, а кушать нечего.
Возвращаемые веб-службами объекты не связаны с серверными за их отсутствием. Потому что у серверной части приложения нет состояния и, соответственно, не может быть никаких объектов в принципе. Обмен сообщениями происходит как и в обычной веб-среде: запрос – ответ без поддержки сессии. Общеупотребительная практика – использование DTO для передачи состояния объектов от клиента к серверу и обратно. Но DTO не содержит никаких ссылок на другие объекты, кроме вложенных. Его структура состоит из полей скалярных типов, разрешённых стандартом, вложенных объектов и массивов. Соответственно, вы не можете прозрачным образом динамически подгрузить недостающий объект, для чего придётся явным образом вызывать службу.