(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Моделирование SOA: Часть 4. Компоновка сервиса

Джим Амсден

В этой статье, четвертой из пяти статей серии, рассказывается о том, как выполнить сборку и согласование поставщиков сервисов, смоделированных в статье "Часть 3. Реализация сервиса", и оркестровку их взаимодействия для того, чтобы создать законченное решение, соответствующее бизнес-требованиям. Компонент, полученный в результате сборки, будет участником сервиса, который объединит в себе сервисы, предоставляемые компонентами Invoicer, Productions и Shipper для того, чтобы предложить новый сервис, который сможет обрабатывать заказы на приобретение. Кроме того, в статье говорится о том, как участники сервиса выполняют исходные бизнес-требования.

Контекст данной статьи

В этой статье мы выполним сборку поставщиков сервиса, созданных в третьей статье серии, чтобы использовать их функциональные возможности в методе еще одного поставщика сервиса. То есть мы создадим новый сервис из комбинации других сервисов. Описанная методика компоновки сервиса допускает неограниченное рекурсивное использование на любом наборе требований и на любом уровне абстракции. Однако здесь мы можем столкнуться с архитектурными ограничениями, которые могут повлиять на детализацию операций сервиса, вопросы безопасности и производительности, объемы обмена данными, выбор протокола связи на проводном уровне и проблемы привязки, что может накладывать ограничения на то, какие сервисы и какими компонентами будут предоставляться. Указанные проблемы определяют архитектуру решения и в этой статье мы не будем их рассматривать. Дополнительную информацию по этой важной теме можно найти в материале "Разработка SOA-решения при помощи ссылочной архитектуры" .

Как и во всех остальных статьях данной серии, мы воспользуемся имеющимися инструментами IBM Rational и IBM WebSphere для создания артефактов решения и их взаимной привязки; это обеспечит нам возможность проверить соответствие нашего решения требованиям и более эффективно управлять изменениями. В таблице 1 перечислены все процессы, которые мы будем использовать при разработке примера, а также инструменты, которые будут использоваться для создания артефактов.

Таблица 1. Роли, задачи и инструменты процесса разработки

Роль Задача Инструменты
Бизнес-исполнитель Формулирование бизнес-задач и целей IBM® Rational® RequisitePro®
Бизнес-аналитик Анализ бизнес-требований IBM® WebSphere® Business Modeler
Разработчик архитектуры программного обеспечения Разработка архитектуры решения IBM Rational Software Architect
Разработчик Web-сервисов Реализация решения IBM® Rational® Application Developer и WebSphere Integration Developer

Пересмотр реализации сервиса

Давайте начнем с пересмотра поставщиков сервисов, которые мы реализовали в предыдущей статье. На рисунке 1 показана схема поставщика сервиса Invoicer.

Рисунок 1. Поставщик сервиса Invoicer.
The Invoicer service provider

Поставщик сервиса Invoicer предоставляет сервис InvoicingProtocol, который предназначен для вычисления первоначальной стоимости заказа на приобретение и дальнейшего уточнения этой стоимости после того, как станет известна информация о доставке. Итоговая стоимость заказа зависит от того, где производятся продукты и откуда они должны быть доставлены. Вычисление первоначальной стоимости можно использовать для того, чтобы проверить, имеет ли клиент достаточный кредит или не отказался ли он от намерения купить продукцию. Лучше убедиться в этом до заполнения формы заказа.

На рисунке 2 показана схема поставщика сервиса Productions

Рисунок 2. Топология сервиса Production
Production service topology diagram

Поставщик сервисов Productions предоставляет сервис Scheduling, предназначенный для определения места и срока изготовления товаров. Эта информация может использоваться для создания расписания доставки, которое будет использоваться при обработке заказов на приобретение.

На рисунке 3 показана схема поставщика сервиса Shipper.

Рисунок 3. Поставщик сервиса Shipper
The Shipper service provider diagram

Поставщик сервисов Shipper предоставляет сервис ShippingService, предназначенный для доставки товаров покупателю по заказу. Этот сервис запрашивает интерфейс ScheduleProcessing , чтобы сделать запрос к потребителю об обработке готового расписания доставки.

Компоновка сервиса

К этому моменту все сервисы предоставляются какими-либо поставщиками, и мы можем использовать их в сборке, которая будет удовлетворять исходным бизнес-требованиям. Такая сборка компонует и оркеструет сервисы в соответствии с бизнес требованиями с целью предоставления метода для сервиса Purchasing. Мы создадим компонент OrderProcessor , который будет предоставлять сервис Purchasing для обработки заказов на приобретение. Этот поставщик сервиса запрашивает сервисы, определяемые спецификациями сервисов InvoicingService, Scheduling и ShippingService. Затем мы создадим компонент OrderProcessing, представляющий собой сборку, состоящую из экземпляров компонентов Invoicer, Productions и Shipper, и компонент OrderProcessor, который предназначен для реализации операции сервиса по обработке заказов на приобретение.

Поставщик сервисов Order Processor

Сервис обработки заказов на приобретение определен в спецификации сервисов Purchasing (также показанной на рисунке 4), которая включает следующую функциональную возможность (или операцию):

+ processPurchaseOrder (customerInfor : Customer, purchaseOrder : PurchaseOrder) : Invoice

Этот сервис будет предоставлять поставщик сервиса OrderProcessor. OrderProcessor - это компонент, предоставляющий сервис посредством согласования других поставщиков сервисов; которые оркеструются в соответствии с контрактом о требованиях. То есть ряд аспектов предоставляемого метода сервиса разрабатывается специально для использования других поставщиков сервисов соответствующим способом. Данный компонент предоставляет интерфейс Purchasing через свой порт сервиса Purchasing. Через этот порт осуществляются все взаимодействия с покупателями, тем самым клиентские компоненты покупателей отделяются от взаимодействий, которые компоненты могут осуществлять с другими потребителями или поставщиками сервисов. Это ограничивает связанность в модели, что упрощает внесение изменений при изменении рынка, потребителей и поставщиков сервиса.

Рисунок 4. Спецификация сервиса приобретения Purchasing.
The Purchasing service specification diagram

На рисунке 5 изображена организация компонента OrderProcessor, отображаемая в представлении обозревателя проектов Project Explorer.

Рисунок 5. Функциональная бизнес-область управления заказами (пакет)
The order management business functional area (package) screen capture

Поставщик сервисов OrderProcessor принадлежит к пакету org::ordermanagement, который используется для организации сервисов, имеющих отношение к управлению заказами. Кроме того, пакет управления заказами содержит соответствующие интерфейсы сервисов, потребителей и поставщиков сервисов.

На диаграмме OrderProcessor (рисунок 6) изображена внешняя схема поставщика сервисов OrderProcessor вместе с предоставляемыми и запрашиваемыми им сервисами. (Запрашиваемые сервисы иногда называют требованиями , это способствует разграничению потребностей и функциональных возможностей.) Внешняя схема или "черный ящик" - это то, что видят потребители поставщика сервисов. Внутренняя структура компонента, которая будет показана далее в этом примере, представляет собой внутреннюю схему, или "белый ящик", структуры, поддерживающей проект реализации компонента.

Рисунок 6. Поставщик сервиса OrderProcessor
The OrderProcessor service provider diagram

Внешняя схема не является спецификацией, существующей отдельно от реализации; это просто представление определенных аспектов компонента. Если архитектор или разработчик захочет полностью отделить спецификацию поставщика сервиса от ее предполагаемой реализации, он воспользуется компонентом specification . Компонент specification определяет контракт между потребителями и поставщиками сервисов, который отделяет их от реализаций конкретных поставщиков. Компонент specification может быть реализован многими конкретными компонентами, которые предоставляют сервисы способом, реализующим контракт и обеспечивающим приемлемое качество сервиса. См. дополнительную информацию в статье "Моделирование SOA: Часть 2. Спецификация сервиса".

Компонент поставщика сервиса OrderProcessor настолько прост и стабилен, что в нашем примере архитектор и разработчик решили не использовать спецификацию сервиса. В результате любой потребитель сервиса, использующий компонент OrderProcessor, будет сцеплен с данной конкретной реализацией. Окажется ли такой проект адекватным, зависит от того, сколько сервисов будет использоваться и сколько сервисов изменится с течением времени. Решение по поводу того, какой уровень связности приемлем для конкретной системы, может принять только архитектор решения.

Компонент OrderProcessor имеет также порты сервиса, представляющие собой требования для потребностей, выставляемые другими поставщиками сервисов: Invoicing, Scheduling и Shipping. Эти поставщики предоставляют сервисы, которые компонент OrderProcessor использует для реализации предоставляемых им операций сервиса.

Каждая из этих точек взаимодействия сервисов связана с простым протоколом, который влияет на предоставляемые и запрашиваемые интерфейсы. Например, точка взаимодействия Invoicing запрашивает интерфейс Invoicing, чтобы инициировать вычисление стоимости и передать стоимость доставки. Однако на вычисление стоимости доставки может потребоваться определенное время, поэтому OrderProcessor предоставляет интерфейс InvoiceProcessing через порт инвойсинга, чтобы поставщик сервиса инвойсинга мог передать счет после того, как он будет готов.

Модель проекта реализации Order Processor

Мы закончили разработку архитектурной структуры модели сервиса и документировали ее в форме внешних схем поставщиков сервиса. Далее нам необходимо разработать метод для операции сервиса processPurchaseOrder, которая предоставляется компонентом OrderProcessor. Метод необходимо согласовать с любыми выполняемыми контрактами сервисов или реализованными спецификациями сервисов, а также со спецификацией сервиса, в которой определена данная операция.

Внутренняя структура

Поставщик сервиса OrderProcessor предоставляет единственную спецификацию сервиса, Purchasing, через свой порт сервиса приобретения. Эта спецификация сервиса определяет единственную операцию, processPurchaseOrder. Поставщик сервиса должен предоставить метод для всех предоставляемых им операций сервиса. В данном примере в качестве метода операции processPurchaseOrder используется Activity. Подробно это показано во внутренней структуре компонента OrderProcessor, предоставляющего сервис. Внутренняя структура OrderProcessor документируется в диаграммах, интерфейсах, классах и деятельностях, как видно из представления Project Explorer на рисунке 7 и комбинированной структурной диаграммы на следующем рисунке 8.

Рисунок 7. Схема организации поставщика сервисов OrderProcessor
The organization of the OrderProcessor service provider diagram

В представлении Project Explorer отображается список компонентов поставщика OrderProcessor и метод (поведение) для каждой предоставляемой операции. В этом примере используется соглашение об использовании диаграммы класса с именем, соответствующим имени компонента, в пакете, содержащем этот компонент, для того, чтобы отобразить внешнюю схему. Эта диаграмма изображена на рисунке 6 и в нижней части рисунка 7. Комбинированная структурная диаграмма с именем, соответствующим имени компонента и содержащаяся в этом компоненте, отображает внутреннюю схему структуры поставщика сервисов. Описанная диаграмма находится сразу под изображением поставщика сервисов OrderProcessor на рисунке 7. Соглашения облегчают координацию внешних и внутренних схем участников сервиса и составление диаграмм, полностью отображающих структуру компонента.

Комбинированная структурная диаграмма OrderProcessor, показанная на рисунке 8, представляет собой общую схему внутренней структуры поставщика сервисов. На ней мы снова видим элементы (порты и свойства), составляющие статическую структуру компонента.

Рисунок 8. Внутренняя структура поставщика сервисов OrderProcessor
The internal structure of the OrderProcessor service provider diagram

Внутренняя структура компонента OrderProcessor не отличается сложностью. Этот компонент состоит из портов сервиса для предоставляемых и запрашиваемых интерфейсов, а также из нескольких других свойств, которые управляют состоянием поставщика сервисов. Свойство ID используется для идентификации поставщика сервисов. В процессе исполнения сервиса это свойство будет использоваться для корреляции взаимодействия потребителей и поставщиков. Свойства schedule и shippingInfo представляют собой информацию, которая используется в реализации операции сервиса processPurchaseOrder.

Методы предоставляемых операций сервиса

Каждая операция сервиса, предоставляемая поставщиком сервиса, должна быть реализована любым из следующих элементов:

  • поведением Behavior (Activity, Interaction, StateMachine или OpaqueBehavior), которое представляет собой метод данной операции;
  • операцией AcceptEventAction (для асинхронных вызовов) или AcceptCallAction (для синхронных вызовов вопрос или ответ) в Activity, принадлежащей компоненту.

Благодаря этому единственная деятельность Activity сможет иметь (как правило) более одной точки параллельного ввода и соответствовать нескольким деятельностям получения, выраженным на языке исполнения бизнес-процессов Business Process Execution Language (BPEL). Операции AcceptEventActions обычно используются для обработки обратных вызовов по поводу возвращаемой информации из других асинхронных операций CallOperationActions.

В компоненте OrderProcessor присутствуют примеры обоих стилей реализации сервисов. Операция processPurchaseOrder имеет метод Activity с таким же именем. Эта деятельность, изображенная на рисунке 9, является поведением, которым владеет поставщик сервисов, предоставляющий операцию сервиса.

Рисунок 9. Реализация операции сервиса processPurchaseOrder
The processPurchaseOrder Service Operation Implementation diagram

Данная диаграмма очень похожа на диаграмму WebSphere Business Modeler для этого же поведения. В данном процессе операции сервиса InvoiceProcessing и ScheduleProcessing реализуются через операции регистрации событий processInvoice и processSchedule. Обратите внимание на то, что соответствующие операции в интерфейсах помечены как trigger , чтобы показать их способность отвечать на AcceptEventActions (аналогично приемкам и AcceptEventActions, где триггером является событие SignalEvent). Ключевое слово trigger не поддерживается UML 2 и включено в схему только для того, чтобы сделать понятнее способ реализации этих операций.

Примечание
Операции не будут приняты до тех пор, пока выполняется деятельность processPurchaseOrder и пока поток управления не выполнит две операции приема вызовов. Это показывает, что реализация операции может определять, когда будет получен ответ на другие операции.

Выполнение контрактов сервиса

Работа над компонентом OrderProcessor завершена. Нам осталось выполнить еще две вещи:

  1. Сначала необходимо снова связать поставщика сервиса OrderProcessor с бизнес-процессом, который моделирует требования к этому поставщику;
  2. Затем мы должны создать подсистему для подключения поставщиков, способную предоставить запрашиваемые поставщиком OrderProcessor интерфейсы, к соответствующим портам сервиса.

В результате мы получим работоспособную развертываемую подсистему. В этом разделе мы займемся связыванием SOA-решения с бизнес-требованиями. В следующем разделе будет рассказано о развертываемой подсистеме.

В этом разделе мы ничего не меняем в способе трансляции компонента OrderProcessor в реализацию SOA. Связывание компонента с контрактами сервиса описывает только то, как компонент выполняет требования, определяемые этими контрактами. Это не оказывает влияния на реализацию поставщика сервисов или на то, как он будет транслирован в SOA-решение. Однако такая компоновка отличается большей сложностью, чем простая взаимосвязь. В частности, она показывает, какие элементы поставщика сервисов выполняют роли в контракте о требованиях к сервису и какие ограничения для компонента способствуют выполнению бизнес-ограничений. Благодаря этому обеспечивается лучшая отслеживаемость, поддержка высокодетализированного управления изменениями и возможность использовать проверку корректности моделей, чтобы гарантировать, что решение действительно выполняет эти требования.

На рисунке 10 показаны требования для поставщика сервисов OrderProcessor при помощи контракта сервиса, который предоставляет ориентированное на роли представление бизнес-процессов, созданных бизнес-аналитиком. Чтобы показать, какой контракт сервиса выполняет поставщик сервисов OrderProcessor, в схему добавлена кооперация.

Рисунок 10. Выполнение контракта сервиса
Fulfilling the service contract diagram

Использование кооперации, которое на рисунке 10 обозначено как contract , представляет собой экземпляр кооперации сервисов Purchase Order Process, которая изображена на следующем рисунке. Это определяет, что поставщик сервиса OrderProcessor выполняет бизнес-требования процесса обработки заказов на приобретение Purchase Order Process. Привязки ролей показывают, какие роли в контракте сервиса соответствуют элементам поставщика сервисов. Например, порт инвойсинга выполняет роль Invoicing, а порт приобретения выполняет роль orderProcessor.

Эти привязки ролей не имеют отношения к коннекторам каналов сервиса, о которых пойдет речь в следующем разделе. Коннекторы каналов сервисов используются для подключения требований потребителей к сервисам поставщика в подсистеме. Привязки ролей определяют, какую роль выполняет данный элемент в контракте сервиса. Интерпретация привязки роли может быть строгой или нестрогой. Строгое выполнение контракта означает, что элемент должен быть совместим с ролью, к которой он привязывается, по типу. Нестрогое выполнение контракта означает, что элементы выполняют роли в соответствии с замыслом архитектора, но проверка корректности модели не сможет верифицировать совместимость роли и элемента. Причина, по всей вероятности, заключается в том, что контракт бизнес-сервиса неполон или что в нем присутствует только неформальная черновая редакция бизнес-требований.

Рисунок 11. Контракт о требованиях к сервису
Service Requirements contract diagram

Чтобы продемонстрировать, как SOA-решение выполняет бизнес-требования, придется дополнительно поработать над определением контракта и привязкой ролей, но в результате мы получим преимущество при управлении изменениями. Чтобы определить, какие поставщики сервиса удовлетворяют бизнес-требованиям, можно использовать запросы к модели. Любые изменения в требованиях, вероятно, потребуют изменения в одной из ролей в кооперации сервисов. Затем разработчику модели можно будет перейти непосредственно к элементам, которые выполняют эти роли, чтобы определить, какие изменения возможно придется внести в спецификацию сервисов, которая типизирует эти элементы, чтобы привести их в соответствие с изменившимися требованиями. Кроме того, можно использовать проверку корректности модели, чтобы определить, были ли внесены в определенные роли такие изменения, в результате которых элементы, выполняющие эти роли в SOA-решении, не смогут больше выполнять все ответственности этих ролей. Эта методика гораздо эффективнее стереотипизированных взаимозависимостей, которые не имеют поддерживаемых семантических значений или слабосвязанной семантики реализации прецедентов. Это такой вид формального, верифицируемого согласования между SOA-решениями и бизнес-требованиями, который гарантирует, что решения будут значимыми для бизнеса, соответствующими бизнес-требованиям; что делает решения динамичными, способными легко адаптироваться к изменениям.

Сборка подсистемы OrderProcessing

Последнее, что необходимо сделать в нашем решении - это создать подсистему OrderProcessing, которая будет использовать поставщики сервиса, реализованные нами к данному моменту, и собрать развертываемое решение из этих элементов.

Подсистема, показанная на рисунке 12, представляет собой развертываемый компонент, который согласует поставщик сервиса OrderProcessor с другими поставщиками сервисов, предоставляющими необходимые сервисы. Это подсистема представляет собой сборку, которая предоставляет всю необходимую для развертывания и исполнения сервиса OrderProcessor информацию.

Рисунок 12. Сборка развертываемой подсистемы из элементов
Assembling the parts into a deployable subsystem diagram

Подсистема OrderProcessing состоит из экземпляров компонентов поставщиков сервисов OrderProcessor, Invoicer, Productions и Shipper. Сервис инвойсинга seller-компонента согласуется с сервисом инвойсинга компонента Invoicer. Это корректное согласование, поскольку спецификация сервиса инвойсинга компонента OrderProcessor является комплементарной спецификацией сервиса инвойсинга поставщика Invoicer. Компонент OrderProcessor запрашивает интерфейс Invoicing, который предоставляется поставщиком сервиса Invoicer. Он также предоставляет интерфейс InvoiceProcessing, позволяющий поставщику Invoicer получать обновленные счета.

Согласование сервисов (экземпляров спецификаций сервисов) означает, что участники согласны взаимодействовать через коннекторы согласно спецификации сервиса, то есть они согласны использовать оговоренный в спецификации протокол. Спецификация сервиса определяет роли, которые согласуемые участники выполняют в этом протоколе. Коннектор каналов сервиса между портом инвойсинга потребителя orderProcessor и портом инвойсинга поставщика Invoicer имеет контракт (некоторое поведение), которое представляет собой поведение InvoicingService спецификации сервиса InvoicingService. Имя коннектора определяется по имени его контракта в соответствии с соглашением. От всех соединений на концах этого коннектора требуется соблюдение данного контракта или протокола. Коннекторы формализуют использование взаимозависимостей в архитектуре сервиса.

Обратите внимание на то, что коннекторы между элементами producer и seller не имеют контракта. Причина заключается в том, что в интерфейсе сервиса Scheduling протокол не определен; следовательно, нет необходимости в контракте для этого коннектора.

Остальные потребители и поставщики согласуются аналогичным образом. Согласуемые сервисы могут предлагать различные стили привязки. Канал сервиса между точками взаимодействия сервисов может определять действующий стиль привязки, который следует использовать.

Мы завершили создание подсистемы OrderProcessing; теперь она готова к развертыванию. Эта подсистема имеет определенные экземпляры поставщиков запрашиваемых сервисов, необходимых для полной реализации сервиса processPurchaseOrder. После развертывания остальные потребители сервиса могут быть привязаны к сервису Purchasing seller- компонента OrderProcessor и вызывать операции сервиса.

Резюме. Что дальше

Мы завершили идентификацию, создание спецификации и реализацию сервисов, потребителей и поставщиков, необходимых для решения бизнес-задач. В результате мы получили технологически-нейтральную, но законченную модель архитектурно смоделированного решения сервиса.

Чтобы действительно запустить это решение, нам необходимо создать реализацию платформы, которая согласуется с решениями архитектурного проекта, документированными в модели сервисов. Мы моли бы создать такое решение вручную, воспользовавшись в качестве ориентира моделью. Но чтобы обеспечить корректную реализацию архитектурного решения, потребовалось бы много времени и высокая квалификация разработчика; при этом работа была бы утомительной и подверженной ошибкам. Безусловно, можно создать решение вручную, и модель могла бы принести большую пользу в качестве направляющей схемы решения. Но наличие законченной, формальной, верифицированной модели дает нам возможность использовать принцип управляемой моделями разработки (model-driven development, MDD), чтобы создать скелет решения из модели, а затем закончить решение, написав подробный код в платформенно-специфичной среде программирования. Это будет темой следующей заключительной статьи данной серии, "Моделирование SOA: Часть 5. Реализация сервиса." В ней мы воспользуемся функцией преобразования UML-SOA в Rational Software Architect, чтобы создать решение Web-сервиса, которое можно будет напрямую использовать в WebSphere Integration Developer для реализации, тестирования и развертывания готового решения.

Ссылки по теме


 Распечатать »
 Правила публикации »
  Обсудить материал в конференции IBM Rational/Telelogic - системный инжиниринг, управление требованиями, изменениями, жизненным циклом ИС, умное управление проектами »
Написать редактору 
 Рекомендовать » Дата публикации: 19.03.2008 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
IBM Rational Functional Tester Floating User License
IBM RATIONAL Clearcase Floating User License + Sw Subscription & Support 12 Months
IBM RATIONAL Quality Manager Quality Professional Authorized User Single Install License + Sw Subscription & Support 12 Months
Rational ClearQuest Floating User License
IBM RATIONAL Rose Enterprise Floating User License + Sw Subscription & Support 12 Months
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
СУБД Oracle "с нуля"
Новые материалы
Каждый день новые драйверы для вашего компьютера!
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
Обсуждения в форумах
Разработка устройств на микроконтроллерах (17)
Профессиональный программист. Основная специализация: МИКРОКОНТРОЛЛЕРЫ, АССЕМБЛЕР для любых...
 
Пишу программы на заказ для студентов (207)
Пишу для студентов на с, с++, паскаль в средах ms visual studio, qt, builder, borland c, delphi....
 
Разработка программ базы данных (17)
Написание прикладных компьютерных программ (базы данных) на заказ. Разработка корпоративных...
 
Пишу программы на заказ профессионально (3073)
Пишу программы на заказ на языках Pascal (численные методы, списки, деревья, прерывания) под...
 
Ищу программиста для написания программы (29)
Ищу программиста ,владеющего Вижуал Бэйсик и программированием в Экселе, для написания...
 
 
 



    
rambler's top100 Rambler's Top100