Какова лучшая практика при определении службы мыла (общая или специфическая операция)? - PullRequest
7 голосов
/ 08 октября 2008

Моя ситуация такова:

У меня есть нормализованная база данных, в которой я храню географическую информацию об аэропортах. Структура:

airport --is in--> city --is in--> country --is in--> continent

Теперь я хочу позволить пользователям администрировать эти данные, не предоставляя им прямой доступ к базе данных. Нам нужно предложить этот интерфейс администрирования через веб-сервис.

Теперь, когда речь идет о разработке сервиса, мы вступили в дискуссию о том, как определить операции. Мы придумали разные решения:

Решение A: конкретные операции

Для каждой из четырех таблиц (аэропорт, город, страна, континент) мы определяем 3 операции:

  • вставить
  • получить
  • обновление

Это приведет к 12 операциям с 2 объектами запроса / ответа = 24 объекта

Чтобы создать новый аэропорт со всеми зависимостями, потребуется как минимум 4 запроса.

Решение B: универсальное

Существует только одна операция, которая контролируется с помощью параметров. Эта операция способна создать все необходимое для администрирования базы данных.

Операция решает, что нужно сделать, и выполняет ее. Если произойдет ошибка, она откатит все назад.

==> 1 операция = 2 очень сложных объекта запроса / ответа

Решение C: Встреча в середине 1

Одна общая операция для таблицы, которая может выполнять get, insert, update, как решение B, но ориентирована на одну таблицу.

==> 4 операции = 8 сложных объектов запроса / ответа

Решение D: Встреча в середине 2

Одна общая операция на действие (получение, вставка, удаление), которая может работать с каждой таблицей и разрешать зависимости.

==> 3 операции = 6 немного более сложных объектов запроса / ответа

* ** 1054 тысяча пятьдесят три * Пример

Поскольку это было довольно абстрактно, вот упрощенный пример создания объектов запроса (JFK / New York / USA / North America):

Решение A:

Запрос 1/4:

<insertContinent>North America</insertContinent>

Запрос 2/4:

<insertCountry continent="North America">USA</insertCountry>

Запрос 3/4:

<insertCity country="USA">New York</insertCity>

Запрос 4/4:

<insertAirport city="New York">JFK</insertAirport>

Раствор B:

Запрос 1/1:

<action type="insertCountry" parent="North America">USA</action>
<action type="insertAirport" parent="New York">JFK</action>
<action type="insertContinent" parent="">North America</action>
<action type="insertCity" parent="USA">New York</action>

Раствор C:

Запрос 1/4:

<countryAction type="insert" parent="North America">USA</countryAction>

Запрос 2/4:

<airportAction type="insert" parent="New York">JFK</airportAction>

Запрос 3/4:

<continentAction type="insert" parent="">North America</continentAction >

Запрос 4/4:

<cityAction type="insert" parent="USA">New York</cityAction >

Решение D: Запрос 1/1:

<insert airport="JFK" city="New York" country="USA" continent="North America" />

Решение D кажется мне довольно элегантным, поэтому я попытался поместить это в XSD:

Код:

<complexType name="NewContinent">
    <sequence>
        <element name="NAME" type="string"></element>
    </sequence>
</complexType>

<complexType name="NewCountry">
    <sequence>
        <element name="ISOCODE" type="string"></element>
        <element name="NAME" type="string"></element>
        <choice>
            <element name="newCONTINENT" type="tns:NewContinent"></element>
            <element name="CONTINENT" type="string"></element>
        </choice>
    </sequence>
</complexType>

<complexType name="NewCity">
    <sequence>
        <element name="IATA" type="string"></element>
        <element name="NAME" type="string"></element>
        <choice>
            <element name="COUNTRY" type="string"></element>
            <element name="newCOUNTRY" type="tns:NewCountry"></element>
        </choice>
    </sequence>

</complexType>

<complexType name="NewAirport">
    <sequence>
        <element name="IATA" type="string"></element>
        <element name="NAME" type="string"></element>
        <choice>
            <element name="CITY" type="string"></element>
            <element name="newCITY" type="tns:NewCity"></element>
        </choice>
    </sequence>

</complexType>

Соответствующий запрос будет выглядеть следующим образом:

<complexType name="Request">
    <choice>
        <element name="AIRPORT" type="tns:NewAirport"></element>
        <element name="CITY" type="tns:NewCity"></element>
        <element name="COUNTRY" type="tns:NewCountry"></element>
        <element name="CONTINENT" type="tns:NewContinent"></element>
    </choice>
</complexType>

Теперь мой вопрос: Это действительно лучшее из доступных решений? Достаточно ли XSD, чтобы понять, что происходит?

Ответы [ 2 ]

5 голосов
/ 08 октября 2008

Предположительно, вы пишете уровень протокола, который будет понимать ваши различные типы сообщений. Вам также понадобится прикладной уровень для анализа содержимого сообщения. Различные подходы, которые вы упомянули, сместят бремя анализа между этими двумя уровнями Так, например:

Решение A : Уровень протокола выполняет весь анализ и возвращает данные и команду. Прикладной уровень может просто использовать данные. Это также называется шаблоном RPC.

Плюсы: вы можете проверить свои сообщения. Вы можете сопоставить сообщения непосредственно вызовам приложения.

Минусы: если вам нужно изменить интерфейс, ваш протокол изменится.

Решение B : Уровень протокола возвращает два значения и команду. Прикладной уровень должен использовать команду для разбора значений на типы.

Плюсы: протокол никогда не меняется.

Минусы: вы не можете проверять сообщения. Код вашего приложения более сложный.

Решение C : Уровень протокола возвращает два известных типа и команду, которую необходимо проанализировать. Прикладной уровень может просто анализировать команду и использовать данные.

Плюсы: я не могу думать ни о каком, кажется, не очень хороший компромисс.

Минусы: оставляет анализ только частично.

Решение D : Уровень протокола возвращает известные типы (как вы это реализовали) и универсальную команду. Прикладной уровень должен просматривать данные, которые он получает, и преобразовывать общую команду в конкретную команду. Это похоже на архитектуру REST.

Плюсы: вызовы - это разные операции, так что вы можете, например, получить кеш-ответы.

Минусы: сложность на уровне приложения

Модель REST обычно реализуется не так, как вы описали. Он использует HTTP GET, POST, PUT, DELETE сообщения для передачи произвольных документов. Параметры приведены как часть URL. Так, например:

<insert airport="JFK" city="New York" country="USA" continent="North America" />

становится

<insert URL="airport?city=Chicago">ORD</insert>

Или, если вы используете HTTP, он становится запросом POST к URL-адресу аэропорта с параметром города с содержимым, представляющим информацию об аэропорте. Обратите внимание, что кое-что из этого становится понятнее с более сложными данными, где у вас есть несколько элементов и смешанных типов. Например, если вы хотите отправить аббревиатуру аэропорта, длинное имя и высоту.

Я думаю, что архитектура REST могла бы работать очень хорошо для интерфейса, который вы описываете. Пока все, что вам нужно сделать, это поддерживать операции CRUD. Есть много сайтов, которые дадут вам плюсы и минусы архитектурного стиля REST.

Лично я предпочитаю стиль RPC (решение A) с некоторыми атрибутами REST. Я хочу, чтобы протокол выполнял анализ и проверял сообщения. Обычно люди реализуют интерфейсы веб-сервисов SOAP.

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

1 голос
/ 17 июля 2010

Это старый вопрос, и я уверен, что сервис написан давно, но я все равно хотел дать ответ.

Подход RESTful заключается в определении ресурса аэропорта, например:

<airport href="/airports/JFK">
    <name>JFK</name>
    <city>New York</city>
    <country>USA</country>
    <continent>North America</continent>
</airport>

Или, если вы хотите использовать совместимый с браузером микроформат:

<div class="object airport" href="/airports/JFK">
    <ul class="attributes"> 
        <li class="name">JFK</li>
        <li class="city">New York</li>
        <li class="country">USA</li>
        <li class="continent">North America</li>
    </ul>
</div>

Этот ресурс будет расположен в URI, подобном /airports/JFK, который будет получен методом GET, обновлен методом PUT и удален методом DELETE.

В такой конструкции URI /airports/ будет представлять ресурс контейнера для всех аэропортов в базе данных, а URI, такие как /airports/?city=New+York и /airports/?country=USA, будут фильтрами для контейнера, возвращающими подмножество аэропорты. Оба из них будут GET методами, и ресурсы будут содержать список ресурсов аэропорта, как определено выше, либо полностью (так как они малы), либо с несколькими полезными атрибутами, а href указывает на полное ресурс для каждого аэропорта.

Наконец, добавление нового ресурса может быть либо методом PUT для полного URI аэропорта, либо методом POST для /airports/. В обоих случаях тело запроса является ресурсом аэропорта, как показано выше. Разница между методами заключается в том, кто принимает решение об окончательном URI для аэропорта: клиент выбирает PUT, а служба - POST. Какой из них вы используете, зависит от того, могут ли ваши клиенты разумно определить правильный URI. Обычно служба принимает решение, потому что URI содержат числовой уникальный идентификатор, и служба должна выбирать это.

Теперь, конечно, ваш первоначальный вопрос был о SOAP, а не REST. Я хотел бы продолжить и настроить дизайн RESTful, как я описал, а затем описать мои ресурсы как сложные типы, используя XSD и службу SOAP с действиями, которые дублируют GET, PUT, DELETE и POST операции службы RESTful. Это даст вам RPC эквивалент:

class Airport
    has String name
    has String city
    has String country
    has String continent
    method void update(name, city, country, continent)
    method void delete()

class AirportList
    method Airport[] get(opt name, opt city, opt country, opt continent)
    method void add(name, city, country, continent)
...