Как сделать хороший API?

ArchimateAbstractВ предыдущем сообщении Event-driven architecture я позволил себе заметить, что сервисная шина (ESB) не является необходимым условием построения сервис-ориентированной архитекутры(SOA). О том, что сервисы должны предоставляться именно приложениями много говорят в SOA School Томаса Эрла, используя термин Intrinsic interoperability На занятиях мы довольно долго пытались его корректно перевести и, по-моему, остановились на «врожденной способностью к взаимодействию». Этот принцип, кстати, вошел в СОА-манифест в формулировке «Intrinsic interoperability over custom integration»
Одним словом, речь идет о том, что задача предоставления [ре]юзабильных повторно-используемых программных интерфейсов отводится именно бизнес-приложениям, а не интеграционной среде. (Под юзабилити я понимаю именно юзабилити, а не эргономику. Чем отличается одно от другого см. Юзабилити глазами архитектора)
Вопрос в том, как такие интерфейсы построить.

Вариант 1. Обнаружить стандарт, описывающий данную предметную область (домен). Я не говорю о стандартах вида WS-* они описывают совершенно другие вещи. Речь идет именно о стандартах CMIS, OSS/J и пр.
Вариант 2. Если стандартов нет, и все знания о предметной области локализованы в головах нескольких посвященных соответствующего тайного ордена, то интерфейс придется разрабатывать. И обычно это делается неправильно. Разработчик системы спрашивает, какие данные вам нужны и предоставляет что-то похожее на то, что вы попросили. В результате получаются хранимые процедуры типа:

СуммВырчкНаУтроGF-ЧудесСтрДураков(Кол-воЗолотых)

О повторном использовании таких сервисов говорить, разумеется, не приходится. Очевидный способ построения повторно-используемых интерфейсов – абстрагирование. Про абстрагирование сказано практически во всех работах по программной инженерии от «Программисткого камня» (рекомендую прочитать эту книжку целиком) до классических работ Г.Буча «Объектно-ориентированный анализ и проектирование», но сказано как-то не очень внятно. Поэтому, вопрос как построить хорошую объектную модель, а значит и reusable API остается открытым. Я тоже не знаю полного ответа на этот вопрос, но готов поделиться некоторыми частными, глубоко субъективными наблюдениями:

  • REST лучше чем SOAP, так как оперирует с данными, а не с поведением, а это всегда проще.
  • Референсные модели предметной области, по крайней мере такие, в которых нормальный человек может разобраться, имеют право на жизнь.
  • Что-то чуть более конкретное, чем «Object-Relations» (см. рисунок, предложенный разработчиками ArchiMate).
  • Семь плюс-минус две сущности. Для того, чтоб понять что такое, например WordPress, достаточно посмотреть на структуру его базы данных. В системах, написанных за деньги и на заказ разобраться в структуре данных практически нереально.
  • Классы предметной области и отношения между ними не должны присутствовать в сигнатурах методов. Они должны вести в справочниках, доступных через тот же самый API. Тогда интерфейс становится самоописанным.

Буду признателен за более внятные мысли на тему построения повторно-используемых интерфейсов.

Как сделать хороший API?: 6 комментариев

  1. «Классы предметной области и отношения между ними не должны присутствовать в сигнатурах методов. Они должны вести в справочниках, доступных через тот же самый API. Тогда интерфейс становится самоописанным.»

    Данный пункт не понял. Можете объяснить на примере?

    1. Постараюсь 🙂

      Предположим, мы делаем интерфейс к каталогу книжного магазина. Функции
      getBookAuthor(…) и
      getBookPublisher(…)
      — содержат в себе сигнатуры предметной области. Если мы захотим расширить набор свойств книжки, например, добавить ISDN, то нам придется выдумывать новые функции.
      Функция: getBookProperty(«author»,…) — смотрится уже лучше. Безусловно, для неё нужно предусмотреть функцию чтения справочника свойств книжки, возвращающую коллекцию свойств. Что-то типа: getBookProperties(…)
      Но еще лучше забыть, что мы торгуем именно книжками и реализовать функцию:
      getItemProperty(«book»,»author»,…), т.к. в этом случае мы смастерим интерфейс для работы с произвольными предметами

  2. Сценарий использования в предложенном вами примере усложняется после введения функций вида getItemProperty.

    С функциями getBookAuthor, getBookPublisher все просто и понятно — открываем WSDL-описание, видим данные функции, вызываем нужную. С getItemProperty же надо получить список Item’ов, получить список Proprty’ей, затем вызвать метод getItemProperty с нужным набором параметров. Кстати, сходу не очень понятно, как в рантайме анализировать список, например, свойств, чтобы выбрать нужное.

    Понятно, что описанный вами подход гораздо гибче. Хотя бы с точки зрения больной темы — версионирования — при добавлении нового свойства ненужно добавлять метод в WSDL-описание, а значит оно не изменится.

    В принципе, если воспользоваться теми же композитными сервисами Oracle Service Bus, то всегда можно привести один подход к другому: выставить наружу те методы которые удобнее для клиента, а на самом деле вызывать методы, определенные сервисом. Если я правильно понимаю, то данная трансформация и является одной из функций ESB.

    1. Я думаю, что WSDL не справился с теми ожиданиями, которые на него возлагали. Он описывает сигнатуры методов, параметры, исключения и привязку, но совершенно лишен какой-либо семантики. Т.е. задача самоописанности интерфейсов решена только в части синтаксиса.

      ESB, конечно, как раз и позволяет трансформировать один из подходов в другой. По сути, две основные функции шины — трансформация и маршрутизация.

  3. >>REST лучше чем SOAP, так как оперирует с данными, а не с поведением, а это всегда проще

    REST лучше если на этапе проектирования/разработки не известны процессы которые будут использовать данные.
    Если же процессы известны, то поведенческие методы (скрывающие сложность) лучше.

    В вашем примере, про магазин, SOA метод «purchase» может скрывать за собой проверку склада, анализ адреса доставки, подготовку счета и т.п.
    При этом этот метод будет использоваться часто, и при изменении внтуреннего поведения (теперь у нас 10-к складов) — клиентская часть как была простой так и останется.

    Ес-но, если бизнес процессы не определены или очень хочется оставить возможность их расширять то нужно оставлять методы CRUD для каждой сущности (REST даже лучше удобнее искать и всегда есть unique-url для объекта). Для того же магазина, возможно понадобиться реализовавать use case — «книга в подарок», «три по цене двух» и т.п.

    Наверное, как-то так.

  4. 1. В SOA не стоит отделять API от бизнес-процессов.
    Если строить API сервисов (данных и утилит), исходя из потребностей реальных бизнес-процессов, карта которых поддерживается в более менее «продуманном» виде (повторное использование процессов, аудит, классификация и т.п.) — тогда то самое API почти автоматически будет соответствовать реальной потребности, не иметь избыточностей, лишних универсальностей, не нужных элементов предметной области и т.п. — а также, API будет повторно-используемо, из-за повторности использования самих процессов.
    2. Сервисы данных Domain (предметной области) — ключевые компоненты.
    Если не сериализовывать весь Domain в Xml (типа TMForum SID), а ограничиваться фрагментами необходимых в процессах данных, их CRUD и событиями изменений (например, построение соответствующей Domain TopicMap событий). Создание справочников и т.п. — попытки создания дополнительного, интеграционного уровня могут быть слишком дороги, особенно, если Domain часто меняется.
    Посмотрите на подход DDD — чем прозрачнее система и её API использует представления реальных объектов Domain, тем более проще её развивать и поддерживать — код более соответствует тем понятиям, которыми оперирует заказчик; и не так важно — сервисы это или бизнес-логика…
    3. Изменения Domain и API.
    Да, по мере развития проекта Domain постоянно меняется и дорабатывается, что может приводить к необходимости изменения и самого API (особенно в свете п.2:) — «разделяй и властвуй»! Проксируя изменяющиеся сервисы через ту же ESB, мы можем использовать шлюзы старого API, скрывая изменения Domain — для потребителей сервисов (процессов или др.) API не меняется, либо меняется постепенно, «когда у них руки дойдут перейти на новую версию API». Т.е. используя шлюзы на уровне ESB мы можем вести 2 типа разработки: развитие сервисов и поддержка интеграции. На самом деле, по п.1 следует, что скорей всего изменения процессов будут создавать стимулы для изменений Domain — таким образом, на момент, когда нужно будет внести изменение в Domain процессы уже будут готовые к изменению API.
    4. События — очень важны и являются частью API

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *