Какова наилучшая практика определения мыльного сервиса (общего или стандартного)?конкретная операция)?

StackOverflow https://stackoverflow.com/questions/180572

  •  05-07-2019
  •  | 
  •  

Вопрос

Моя ситуация следующая:

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

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

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

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

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

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

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

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

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

Решение Б:универсальный

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

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

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

Решение С:Встречаемся посередине 1

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

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

Решение Д:Встречаемся посередине 2

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

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

Пример

Поскольку это было довольно абстрактно, вот упрощенный пример объектов-запросов для создания (JFK/Нью-Йорк/США/Северная Америка):

Решение А:

Запрос 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>

Решение Б:

Запрос 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>

Решение С:

Запрос 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 >

Решение Д:Запрос 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, чтобы понять, что происходит?

Это было полезно?

Решение

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

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

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

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

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

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

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

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

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

Минусы:Оставляет анализ выполненным лишь частично.

Решение Д:Уровень протокола возвращает известные типы (то, как вы их реализовали) и общую команду.Уровень приложения должен просмотреть полученные данные и преобразовать общую команду в конкретную команду.Это похоже на архитектуру 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, и вы обнаружите, что вклиниваете его в существующие четыре сообщения.

Другие советы

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

Подход 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)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top