Недавно мы перезапустили API Яндекс.Кассы – платежного сервиса с 15-летней историей. Я хочу рассказать, как решить такую амбициозную задачу. Материала набралось на серию статей, поэтому здесь я подробно расскажу о проектировании, переработке наших API, а также про наши инструменты и процессы.


Ключевые слова для оценки полезности: API, REST, OpenAPI, Swagger, рефакторинг взаимодействия систем.


Постановка проблемы, Или о чем этот разговор


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


  • У разных подразделений свои планы, и не всегда можно делать работу одновременно. Договариваться о порядке взаимодействия и документировать его приходится на берегу.
  • Важно сохранить эталонное знание о правилах работы сервиса и интеграции с ним. Это гораздо сложнее, если не зафиксировать порядок взаимодействия сторон.

Методологии разработки вроде Scrum позволяют ненадолго снять эти проблемы:


  • Сложность некоторых задач выходит за рамки одной команды и их нужно решать сообща.
  • Есть задачи, которые нельзя однозначно отнести только к одному из продуктов, они требуют согласованных изменений в нескольких компонентах системы.
  • Задачи сопровождения требуют доработки старых сервисов. Настолько старых, что знания о них потеряли былую яркость.

За десять лет разработчики Яндекс.Денег убедились в этом, пройдя путь описания взаимодействий от Wiki-страниц, через XML schema, JSON schema до OpenAPI/Swagger.


Инструменты проектирования спецификаций API – вначале было слово


Все началось с описания порядка взаимодействия сервисов в Wiki и Microsoft Word, с примерами запросов и ответов. Для передачи данных, как правило, использовался XML. Это уже было лучше чем ничего, но такой способ документирования годится только для передачи знаний от человека к человеку.


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


Формальное описание операций, типов данных и их граничных условий нужно не только для разработки, но и для автоматизации модульного тестирования. Первые формальные контракты API-сервисов мы описывали в формате XML schema, позже пробовали и JSON schema. Но оба не идеальны, что мешает полностью перейти на них с текстовых описаний Wiki или Microsoft Word:


  • В XML schema невозможно добавить полноценную документацию с описанием сценариев работы, ее приходится вести отдельно.
  • JSON schema – слишком сырая спецификация без достаточной инструментальной поддержки, тоже не позволяет включать полноценную документацию.

Нам идеально подошел OpenAPI, ранее известный как Swagger. OpenAPI Initiative это открытый проект под управлением Linux Foundation. Я убежден, что его ждет большое будущее.


OpenAPI 3 позволяет в одном файле спецификации объединить документацию и описание формата взаимодействия. Это очень важное качество — у вас никогда не будет рассинхронизации текстовой документации и файла спецификации API.


В наших проектах используются OpenAPI 3 файлы спецификаций в формате YAML, и вот почему:


  • Человеку YAML читать удобнее, чем JSON.
  • Он включает документацию в формате CommonMark (в прошлом Markdown). Это форматирование, списки, таблицы, цитаты, примеры кода.
  • Документация добавляется к нужному объекту спецификации, а не пишется в отдельных разделах.

Наши инструменты для написания OpenAPI-файлов спецификаций

Декомпозиция сервисов и управление изменениями


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


Спецификация OpenAPI – это текстовый файл в формате YAML, а значит, с ним можно работать как с кодом:


  • Использовать системы контроля версий.
  • Гибко управлять изменениями на основе ветвей, тэгов, релизов.
  • Параллельно делать изменения для множества проектов множеством людей.
  • Построить процедуры ревью и приемки изменений в основной документ.
  • Ссылаться на конкретные версии документа в контексте определенного проекта.

В наших проектах используется система контроля версий Atlassian Bitbucket c плагином Web Pages. Это позволяет одновременно работать со спецификацией, как с кодом, и видеть собранную документацию в HTML-формате.


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


За каждым сервисом API у нас стоит определенный бизнес-процесс, и каждый сервис API описывается отдельным файлом OpenAPI-спецификации. А за него отвечает своя продуктовая команда. С учетом этого, в наборе файлов спецификаций OpenAPI каждый файл соответствует своему прикладному продукту.


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


Файлы спецификаций сгруппированы по целевым продуктам:


  • API-спецификации одного продукта размещаются в одном репозитории.
  • API разных продуктов размещаются в разных репозиториях.

Иными словами, один репозиторий соответствует одному продукту – команде, ответственной за процессы развития и сопровождения бизнес-процессов продукта и спецификаций его API.


По результатам множества выполненных проектов, структура Git-репозитория стала следующей:


  • Ветвь master отражает состояние актуальной спецификации, которая де-факто находится на боевой системе и доступна для использования.
  • Ветвь prototype предназначена для эскизных проектов, для проработки набора сценариев использования продукта. В дальнейшем может быть полезна для ретроспективы начальных построений, которые легли в основу последующих технических решений.
  • Проектные задачи разрабатываются в feature-ветвях. Одна feature-ветвь представляет собой один проект или задачу по модификации API.
  • Проект содержит файл index.html с настройками инициализации Swagger-UI. Благодаря плагину Web Pages это позволяет отображать документацию в виде HTML. Таким образом, каждая ветвь репозитория отображает HTML-документацию онлайн, и на нее можно ссылаться из внешних систем и документов.

Кроме структуры, пришлось разработать и правила работы с репозиторием:


  • Прав на запись в master- и prototype-ветви нет ни у кого, прямая запись запрещена. Все изменения спецификации оформляются как Pull Requests из feature-ветвей.
  • Чтобы изменения из Pull-Request попали в основную спецификацию, этот Pull-Request должен получить Approve от всех обязательных ревьюеров и от любого количества необязательных ревьюеров.
  • Для обсуждения и согласования изменений отлично подходят Pull-Requests, история которых представляет собой историю задач, вносящих изменения в спецификацию.
  • Возможно внесение любых изменений в feature-ветвь на любом этапе существования Pull-Request, однако новые изменения потребуют повторного согласования с ревьюверами.
  • Слияние Pull-Request в master-ветвь осуществляется при выпуске рабочего релиза в боевую эксплуатацию.

Благодаря этим принципам мы получили удобную, прозрачную и предсказуемую среду работы со спецификациями.


Подход Design-first как основа качественного решения задачи


При проектировании новых сервисов мы опираемся на принципы REST, но не соблюдаем их в полной мере – например, если это усложняет архитектуру сервиса или противоречит здравому смыслу.


Ценность REST в том, что этот подход обязывает произвести декомпозицию сервиса на набор сущностей и действий с ними.

REST – удобное отражение подхода к проектированию Domain Driven Design. На мой взгляд, благодаря REST-архитектуре у нас получаются более простые и качественные объектные модели прикладных задач, если сравнивать с тем, что мы ранее делали при помощи RPC-подходов.


Чтобы создать качественный продукт, разработчику требуется тщательно изучить предметную область задачи вне контекста опыта предыдущих проектов. Поэтому Design-first хорошо зарекомендовал себя как подход решения прикладных задач. Его можно разбить на два этапа:


  1. Изучение, описание, а также формализация сущностей и процессов предметной области.


  2. Формализация сценариев использования вашего API-сервиса с учетом возможных связей с другими сервисами. Используйте термины предметной области, а не внутренний технический жаргон — так вы избежите неверного понимания работы вашего сервиса со стороны его пользователей.

Результатом этой работы будет спецификация API сервиса, которая:


  • Определяет набор сущностеи? предметнои? области и их атрибуты.
  • Определяет жизненныи? цикл и набор состоянии? каждои? сущности.
  • Определяет набор деи?ствии? с каждои? сущностью и возможные ошибочные ситуации

Я бы не рекомендовал подход Code-first, когда спецификация API генерируется из существующей реализации: она всегда наследует решения из предыдущих проектов, а не решает задачу. Не существует известных методов решить парадокс курицы и яйца – для создания качественной реализации сперва следует изучить предметную область и провести проектирование сервиса, невозможно создать требуемую реализацию раньше, чем проведено исследование задачи и проектирование.

Антипаттерны REST


Вдохновившись статьей «REST — это новый SOAP», я хотел бы поделиться практическими соображениями по теме примеров некорректного применения REST – ведь это не серебряная пуля и, надеюсь, не золотой молоток в ваших руках.


REST это не только CRUD


Просто удивительно выглядит стремление коллег по цеху упаковать все процессы в модель Create-Read-Update-Delete. Жизнь сложнее и богаче: бизнес-процессы могут состоять из множества операций, сущности могут иметь множество состояний и переходов между ними.


Многие статьи о REST ссылаются на работу Roy Thomas Fielding «Architectural Styles and the Design of Network-based Software Architectures» как первоисточник определения REST (смотрите пятую и шестую главы этой работы). Рекомендаций использовать http- глаголы GET-POST-PUT-DELETE как единственный способ определения операций вы там не найдете.


REST – это принцип декомпозиции сервисов на набор ресурсов и операций над ними. Если вы реализуете всю требуемую функциональность на базе только POST запросов, то это тоже будет REST.

Отражение ошибок бизнес-логики на основе HTTP status


Помните о том, что HTTP-протокол осуществляет транспортную функцию по доставке данных в запросах и ответах. Не следует смешивать уровни бизнес-логики и передачи данных. Четко разделяйте понятия «http-запрос» и «действие бизнес-процесса». HTTP-status коды предназначены для отражения состояния выполнения запроса HTTP, их не следует использовать для задач уровня бизнес-логики.


Тем не менее, мы часто используем сопутствующие протоколы HTTP, которые накладывают свои обязательства на формат ответа, например:



Вот некоторые типовые ситуации, в которых уместно использовать HTTP status:


  1. 400: Неверный формат запроса, формат аргументов запроса.
  2. 401: Отсутствует аутентификация клиента.
  3. 403: Недостаточна авторизация клиента.
  4. 5xx: Техническая ошибка выполнения запроса, недоступность сервиса или промежуточного шлюза.

Отказы уровня бизнес-логики следует отражать как атрибуты сущности, над которой выполнялась операция этим HTTP-запросом.


К примеру, отказ в проведении платежа может быть обусловлен недостатком средств на счете клиента. При этом все http-запросы для проведения платежа будут выполнены успешно. Такую ситуацию следует отражать в виде атрибутов состояния сущности «Платеж».


Использование PUT/PATCH-запросов для операций бизнес-логики


PUT-запросы не всегда подходят для операций бизнес-логики, так как они предназначены для замещения документа на сервере новым документом того же типа и структуры. GET-запрос к тому же ресурсу должен возвращать аргумент PUT-запроса.


Стандарт определяет PUT-запрос как:


The PUT method requests that the state of the target resource be сreated or replaced with the state defined by the representation enclosed in the request message payload.

RFC 7231 sec 4.3.4

Пример корректного применения PUT-запроса — загрузка файла на Яндекс.Диск.


RFC 5789 PATCH-запрос тоже ограничен в применимости: его семантика аналогична PUT, тело запроса представляет собой RFC 6902 JSON patch документ, а не любой документ вообще.


Пример JSON patch документа:


[
  {
    "op": "remove",
    "path": "/a/b/c"
  },
  {
    "op": "add",
    "path": "/a/b/c",
    "value": [
      "foo",
      "bar"
    ]
  },
  {
    "op": "replace",
    "path": "/a/b/c",
    "value": 42
  },
  {
    "op": "move",
    "from": "/a/b/c",
    "path": "/a/b/d"
  }
]

Согласитесь, описать операции бизнес-логики простым и доступным способом с таким синтаксисом будет непросто.


Итак, если ваша задача удовлетворяет выше указанным требованиям, используйте PUT/PATCH, в противном случае лучше применять POST.


Пару слов в заключение


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


Надеюсь, представленный материал был вам полезен. Смотрите также мой доклад о проектировании REST-like API на JavaJam, вот ссылка на запись.

Комментарии (39)


  1. PaulMaly
    23.01.2018 19:58

    Мне кажется немного запутанно получилось со Swagger и OpenAPI. По всей видимости имелась ввиду сама спецификация Swagger Specification, которая сейчас действительно называется OpenAPI Specification.


    1. romkavt Автор
      23.01.2018 20:22

      Оригинал это именно OpenAPI Specification, Swagger.io вошел в консорциум Open API Initiative и обеспечил реализацию этой спецификации.


      1. PaulMaly
        23.01.2018 20:33

        Судя по всему сам Swagger так не считает: «At the heart of the above tools is the OpenAPI Specification (formerly called the Swagger Specification).» swagger.io


        1. romkavt Автор
          23.01.2018 20:54

          OpenAPI initiative тоже имеет свою версию: A Short History of the Open API Initiative and the OpenAPI Specification

          On Nov. 5, 2015, SmartBear in conjunction with 3Scale, Apigee, Capital One, Google, IBM, Intuit, Microsoft, PayPal, and Restlet announced the formation of the Open API Initiative, an open source project under the Linux Foundation. As part of the formation of the OAI, SmartBear donated the Swagger specification to the Linux Foundation, meaning that the OpenAPI Specification is semantically identical to the specification formerly known as the Swagger 2.0 specification.


          1. PaulMaly
            23.01.2018 21:57

            Ну так, из текста также ясно следует, что «Swagger specification» была до создания «Open API Initiative». Очевидно «Swagger 2.0 specification» легла в основу для OAI.


    1. InoMono
      23.01.2018 22:03

      Мне кажется немного запутанно получилось со Swagger и OpenAPI. По всей видимости имелась ввиду сама спецификация Swagger Specification, которая сейчас действительно называется OpenAPI Specification

      Наверное, автор слишком уж "в теме" и не отдает себе отчет, что для новичков в этом вопросе не очевидно:


      Изначально это был Swagger
      но уже 2 года как переименован в OpenAPI.


      Тем не менее осталось куча упоминаний о старом названии (например, в названии инструментов, например, существуют и сайт Swagger и сайт OpenAPIs)


  1. VolCh
    23.01.2018 20:27

    Помните о том, что HTTP-протокол осуществляет транспортную функцию по доставке данных в запросах и ответах.

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


    1. romkavt Автор
      23.01.2018 20:55

      Абсолютно верно. Следует разделять слои транспорта и бизнес-логики. REST это про декомпозицию сущностей и операций над ними в набор ресурсов, а не про HTTP-глаголы и наборы http status кодов.


      1. VolCh
        23.01.2018 21:53

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


  1. Pilat
    23.01.2018 22:57

    Делали ли вы генерацию кода по swagger спецификации, и делали ли автоматический рефакторинг при изменениях в спецификации?


    1. romkavt Автор
      24.01.2018 11:34

      Пробовали некоторые подходы, но в итоге у нас это не прижилось в большинстве проектов. Автоматический генератор кода создает лишь структуры данных, но не поведение. Реализация правильного поведения есть основная часть работы. Конечно, для бинарных протоколов наподобие gRPC или GraphQL, генератор кода из схемы абсолютно необходим.


      1. apapacy
        24.01.2018 12:05

        graphql не бинарный протокол. И генератор кода ему вообще не нужен. Есть попытки сделать скафо
        лдинг по переводу в SQL. Но это не генератор кода.


  1. apapacy
    23.01.2018 23:51
    +1

    Многие статьи о REST ссылаются на работу Roy Thomas Fielding «Architectural Styles and the Design of Network-based Software Architectures» как первоисточник определения REST (смотрите пятую и шестую главы этой работы). Рекомендаций использовать http- глаголы GET-POST-PUT-DELETE как единственный способ определения операций вы там не найдете.


    Очень верное замечание. Есть REST — без которого интернет сейчас бы не был интернетом. И есть RESTAPI в примитивной трактовке. RESTAPI (CRUD/G-P-P-D) первоначально поражает своей идеей. Потом начинаются вопросы. Если объект имеет связи (а это общий случай) — как понять, когда включать связанне объекты, а когда не включать. До какого уровня «разворачивать» связанные объекты. Какие поля включать в ответ. Или если сказать проще, у RESTAPI нет необходимого разнообразия

    Моя мечта начать использовать graphql, который тоже REST, но не RESTAPI. С моей точки зрения graphql имеет уникальные перимущества которе не может дать на сегодняшний день ни одна другая система.
    • Программный код и документация связаны и никогда не могут рассогласоваться
    • Клиент сам решает какие поля вернет запрос и какой уровень вложенности объектов ему нужен
    • Клиент может в одном запросе запрашивать произвольное количество объектов (веб-разработчики уже свыклись с тем что запросов будет много, а вот для мобильных разраотчиков один запрос — один экран это было бы неплохо)


    А так же полностью поддерживаю что ни в коем случае нельзя смешивать уровень бизнес-логики и протокола — это о «краноречивых» статусах. Особенно 404 — это нет зебры в зоопарке, или это нет url «зоопарк»?

    Я согласен, что сейчас все вспомогательные библиотеки так или иначе ориентированы на статусы. Но мне кажется логичным использовать максимум три из них: 200 и 304 при нормальном ответе плюс какой-нибудь незарезервированный 400-й статус при ошибке.


    1. InoMono
      24.01.2018 07:58

      С моей точки зрения graphql имеет уникальные перимущества которе не может дать на сегодняшний день ни одна другая система. Программный код и документация связаны и никогда не могут рассогласоваться


      Поясните.
      По мне так это уже давно норма.
      И не вижу чтобы конкретна эта технология тут чем то особенным выделялась.


      1. apapacy
        24.01.2018 12:11

        В graphql спецификация сервиса это одновременно и исполняемый код и документация. Если Вам известны другие подобные системы назовите пожалуйста их


    1. romkavt Автор
      24.01.2018 11:34

      GraphQL безусловно отличный продукт, но имеет свою, ограниченную область применения. Специфический протокол graphql пока поддерживается не всеми языками и платформами, а для публичного API крайне важно дать инструмент абсолютно совместимый со всеми платформами и языками. Интеграция с HTTP+JSON на сегодняшний день абсолютно универсальна.


      1. apapacy
        24.01.2018 12:15

        graphql имеет библиотеки для всех популярных языков. кроме того это не бинарный протокол и запрос можно отправить методом get post в простой текстовой форме


      1. VolCh
        24.01.2018 13:18

        GraphQL — это скорее язык, чем протокол. На практике "по умолчанию" использует те же HTTP+JSON как транспортный протокол и тип данных. Собственно различия начинаются где-то на уровне как интерпретировать поля в JSON. То есть, обычно, в RESTish HTTP API тело HTTP-запроса или одного из его полей верхнего уровня — это представление какой-то сущности, а в GraphQL — собственно GraphQL запрос и его параметры.


  1. dmitry_dvm
    24.01.2018 00:12

    Я правильно понимаю, что swagger-like документацию можно делать отдельно от самого апи и можно просто перенести существующий гуглдок-ад в нормальный вид? Понятно, что все плюшки автогенерации теряются, но все же.


  1. apapacy
    24.01.2018 01:28

    Да, OpenAPI это язык описания спецификаций. Можно в редакторе создавать схему. Чтобы было понятнее вот пример editor.swagger.io как это происходит. Схема выглядит немного устрашающе но есть тулзы например веб-редактор mermade.github.io/openapi-gui Проект молодой но очень интересный.


  1. rostmefpoter
    24.01.2018 11:21
    -2

    не стал даже читать. что такое «REST-like»? давайте уже говорить на одном языке.


    1. VolCh
      24.01.2018 13:22

      Полной реализацией REST HTTP API обычно никто не занимается чисто из практических соображений, поэтому используется REST-like или RESTish когда нарушают некоторые требования или традиции, сложившиеся вокруг REST.


      1. apapacy
        24.01.2018 16:14

        Что такое Полная реализация REST HTTP API? Я уверен что ее не существует в природе. Есть стандарт HTTP и его реализация. Есть стандарт OpеnAPI и его реализщация. О REST HTTP API модно сказать что есть стандарт. Все проблем REST HTTP API как раз и заключается в том что стандарта REST HTTP API так и не получилось. И все попытки продвинуться дальше простых CRUD-запросов к ровно одной таблице (без JOIN-ов) приводят к химерным запросам, при этом у каждого разработчика — своих запросов. Если к этомуц прибавить время на споры какой статус будет правильным если номер телефона не прошел валидацию и как передавать в посте связанные иденификаторы ({customerId: 16) or {customer: {id: 16}) то потери превысят все мвслимые лимиты. Не ну все так хорошо начинается обычно на REST HTTP API. ToDoApp и все такое.


        1. bjornd
          24.01.2018 17:34

          Какие есть варианты решения этой проблемы?


          1. apapacy
            24.01.2018 18:25

            По этому вопросу есть переводная статья на Хабре habrahabr.ru/post/265845
            Ну и язык запросов graphql. Если пытаться сделать RESTAPI выразительнее (через разные параметры типа списка запрашиваемых полей, неободимости разворачивавть связанные объекты, фильтры, пагинацию) — то вся простота и изящества куда-то очень быстро уходит. См. апример www.ibm.com/support/knowledgecenter/SSPLFC_7.2.2/com.ibm.taddm.doc_7.2.2/SDKDevGuide/r_cmdbsdk_restapi_objectclass.html


        1. VolCh
          25.01.2018 09:39

          Я не уверен, поэтому написал "почти". Избегаю кванторов всеобщности, если не уверен.


          Под полной реализацией REST HTTP API я имею в виду API, полностью реализующий как стандарт HTTP, так и принципы REST. Я не уверен даже, что есть полные реализации HTTP и следует ли из полной реализации HTTP автоматическое соблюдение REST (скорее нет, чем да).


          1. apapacy
            25.01.2018 12:05

            Ну так давайте сейчас прямо проверим. Принципы REST дотсупны всем www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm. Хотя я не уверен что их читали 50% тех кто пишет руководства по RESTFullAPI (тут я как раз квантора всеобщности не употребляю если что).
            + 5.1.2 Client-Server — да http протокол предполагает общение клиент-сервер.
            + 5.1.3 Stateless — да http ничего не знает о состоянии клиента. Веб-приложения «научились» нарушать это правило при помощи сессий. И тут же получили оплеуху в виде невозможности масштабироваться. (Поэтому сейчас все чаще можно встретить сессии в виде GWT когда сессия передается в каждом запросе и может быть таким образом расшернеа между серверами)
            + 5.1.4 Cache. Да в http протоколе определены запросы которые кэшируются и которые не кэшируются и как это реализовать.
            ± 5.1.5 Uniform Interface.
            * identification of resources + да идентифицируются по URI
            * manipulation of resources through representations — это как бы не входит в http протокол и может трактоваться как данные и представление это не одно и то же. То есть для простого текста данные==преставление. Для html документа, картинки, скрипта браузер сам знает как отобразить эти данные.
            * self-descriptive messages — это тоже не уровень http протокола. Означает что все для обработки сообщение должно содержаться в самом сообщении.
            * hypermedia as the engine of application state (HATEOAS) — в RESTAPI почему-то не получило широкого распространения.
            + 5.1.6 Layered System Обращаясь к серверу клиент не знает сколько уровней имеется в архитектуре сервера — ну да.
            + 5.1.7 Code-On-Demand — Все что нужно еще закгружается с сервера по требовнию (да, скрипты же загружаются)

            Почему-то ни в одном(!) руководстве не упоминается еще два основополагающих принципа:
            5.1.8 Style Derivation Summary — который гласит что эти принципы предназначены для выбора архитектуры приложения (а не способов формирование url).
            и те упоминается принцип:
            5.1.1 Starting with the Null Style — который гласит что нет других никаких ограничений.

            Я к чему это все привел. Мое убеждение что есть REST и есть RESTAPI. И это две разные совершенно несвязанные вещи.


            1. oxidmod
              25.01.2018 13:11

              Поэтому сейчас все чаще можно встретить сессии в виде GWT когда сессия передается в каждом запросе и может быть таким образом расшернеа между серверами


              Сессия не нарушает Stateless. В контексте self-descriptive messages идентификатор сессии является частью необходимого для выполнения запроса. Мешают масштабироваться только файловые сессии (хотя и их можно вынести на шареную ФС). Если же использовать бд\редис\мемкеш то никаких проблем с масштабированием сессий не возникает.

              hypermedia as the engine of application state (HATEOAS).
              Я бы не сказал, что не прижилось github.com/search?utf8=?&q=hateoas
              Есть как либы для бекенда, формирующие ответ, так и клиентские.


              1. apapacy
                25.01.2018 13:31

                я согласен что передавая токен сессии соблюдается rest а иначе как бы это работало. Но вот по поводу сессий в redis я могу предполагать что будет примерно равный счёт с gwt пока инстансы сервисов запускаются на одном сервере но когда к redis придётся обращаться через сеть — не уверен что будет более масшабируемо. По hatoas какой вывод можно сделать по перейдя по ссылке. что разработки есть. каков их процент использования в resapi реальных сервисах остаётся вопросом.


                1. oxidmod
                  25.01.2018 13:59

                  Если бы никто не использовал, то и либы не писались бы. Да, не все это юзают, но тем не менее.

                  Да и сетевые задержки в пределах ДЦ (между приложением и редисом) — капля в море, по сравнению с задержками между клиентом и ДЦ


              1. VolCh
                26.01.2018 11:20

                Серверная сессия обычно нарушает Stateless, поскольку часто в сессии хранится дополнительная информация, например userId, необходимая для обработки запроса. Да, бывает, там хранится какая-то информация без которой запрос вполне может быть обработан, например кеш тех же прав пользователя (нет сессии — получим из базы или из сервиса авторизации) или информация для трекинга пользовательского поведения, но обычно всё же там хранится какое-то состояние сильно влияющее на результаты обработки запроса.


            1. VolCh
              26.01.2018 11:11

              manipulation of resources through representations — это как бы не входит в http протокол и может трактоваться как данные и представление это не одно и то же. То есть для простого текста данные==преставление. Для html документа, картинки, скрипта браузер сам знает как отобразить эти данные.

              Представление в терминах REST — это не отображение пользователю, а поток байт, отображающий состояние ресурса (5.2.1.2).


              self-descriptive messages — это тоже не уровень http протокола. Означает что все для обработки сообщение должно содержаться в самом сообщении.

              Отчасти вы правы, но только отчасти. Из 5.3.1 "REST enables intermediate processing by constraining messages to be self-descriptive:… standard methods and media types are used to indicate semantics and exchange information", HTTP довольно подробно описывает стандартные методы как синтаксически, так и семантически.


              hypermedia as the engine of application state (HATEOAS) — в RESTAPI почему-то не получило широкого распространения.

              Тем не менее имеется.


              А вообще я как-то потерял нить обсуждения. Моё убеждение, что есть REST как архитектурные принципы построения масштабируемых распределенных приложений, есть более-менее соответствующие этим принципам приложения, подавляющая часть из которых использует HTTP для предоставления серверного API не только в качестве транспортного протокола, но и прикладного, пытаясь его синтаксис и семантику применять к своей предметной области, все эти DELETE /contracts/123. При этом частенько принципы REST и семантика HTTP нарушаются "по мелочам" типа отправки запросов PUT /contracts/123/approve c телом true. Такие API часто называют REST-like (субъективно характерно для русскоязычного информпространства) или RESTish (англоязычные обычно), имея в виду что они довольно близко подходят как к соблюдению принципов REST в архитектуре, так и соблюдение синтаксиса и семантики HTTP (опуская HTTP для удобства). Также можно встретить REST over HTTP, где HTTP используется лишь в качестве транспортного протокола, практически игнорируя его семантику, учитывая лишь технические возможности и особенности поведения распространенных или целевых компонентов сетевой инфраструктуры таких как браузеры, веб-сервера, прокси-сервера и т. п., при этом обычно меньше отходя от принципов REST. Ну и "over HTTP" часто опускают для краткости тоже, поскольку реализации REST не на базе HTTP встречаются крайне редко.


              Получается спор наш чисто терминологический?


              1. apapacy
                26.01.2018 12:49

                Нить разговора следующая. RESTAPI это не REST. Поэтому тот кто говорит REST это хорошо следовательно RESTAPI это хорошо допускает подмену понятий


                1. VolCh
                  26.01.2018 12:59

                  RESTAPI в моих терминах, изложенных в предыдущем комментарии, это что?


                  1. apapacy
                    26.01.2018 13:26

                    мы говорим о RESTAPI не в Вашем комментарии а о трактовке этого термина в повседневной практике разработчиков фактически CRUD


                    1. VolCh
                      26.01.2018 13:44

                      В моих терминах это REST-like или RESTish


  1. undestroyer
    24.01.2018 13:06

    К сожалению ни Swagger, ни RAML не могут сгенерить хорошую статичную документацию, которую не стыдно выложить в общий доступ. Особенно если речь идет об отправке данных как multipart/form-data или application/x-www-form-urlencoded в теле POST запроса в стиле EntityName[attribute]=value. OAPI 3 умеет такое в GET параметрах, заголовках, путях и куках, но не в POST :(
    (структура EntityName определена в отдельном месте для переиспользования)


  1. Frank59
    24.01.2018 20:56

    Уточните, пожалуйста, как вы именно ведете разработку.Верно ли я понял, что в каждом сервисе есть файлы контроллеров+дтошек и отдельный файл спецификации OAPI3 и когда нужно произвести изменение, оно проводится и там и там руками? Или вы все-таки используете автогенерацию документации или наоборот генерацию дтошек на основе OAPI описания?


    1. romkavt Автор
      25.01.2018 13:24

      У каждого сервиса свои нюансы. Контроллеры и DTO у нас есть. Кроме формата сообщений есть и поведение сервиса, эта часть автогенерацией кода пока не решается. Задача полностью автоматической валидации формата и поведения у нас пока не решена, но я думаю, при наличии положительны результатов, это будет материал для отдельной статьи.