Недавно мы перезапустили API Яндекс.Кассы – платежного сервиса с 15-летней историей. Я хочу рассказать, как решить такую амбициозную задачу. Материала набралось на серию статей, поэтому здесь я подробно расскажу о проектировании, переработке наших API, а также про наши инструменты и процессы.  Ключевые слова для оценки полезности: API, REST, OpenAPI, Swagger, рефакторинг взаимодействия систем. Когда маленький коллектив превращается из стартапа в большую компанию, весь объем знаний о программных компонентах и их взаимодействии в уме не удержишь. Отсюда две сложности: Методологии разработки вроде Scrum позволяют ненадолго снять эти проблемы: За десять лет разработчики Яндекс.Денег убедились в этом, пройдя путь описания взаимодействий от Wiki-страниц, через XML schema, JSON schema до OpenAPI/Swagger.  Все началось с описания порядка взаимодействия сервисов в Wiki и Microsoft Word, с примерами запросов и ответов. Для передачи данных, как правило, использовался XML. Это уже было лучше чем ничего, но такой способ документирования годится только для передачи знаний от человека к человеку.  Произвольное текстовое описание может неоднозначно интерпретироваться людьми из разных подразделений, поэтому потребовалась единая система понятий и терминов. По мере роста количества разработчиков стала необходимой и формализация описаний взаимодействий. Формальное описание операций, типов данных и их граничных условий нужно не только для разработки, но и для автоматизации модульного тестирования. Первые формальные контракты API-сервисов мы описывали в формате XML schema, позже пробовали и JSON schema. Но оба не идеальны, что мешает полностью перейти на них с текстовых описаний Wiki или Microsoft Word:  Нам идеально подошел OpenAPI, ранее известный как Swagger. OpenAPI Initiative это открытый проект под управлением Linux Foundation. Я убежден, что его ждет большое будущее.  OpenAPI 3 позволяет в одном файле спецификации объединить документацию и описание формата взаимодействия. Это очень важное качество — у вас никогда не будет рассинхронизации текстовой документации и файла спецификации API. В наших проектах используются OpenAPI 3 файлы спецификаций в формате YAML, и вот почему: Типовая проблема, с которой сталкиваются многие организации – управление изменениями документов. Случается так, что параллельно существует множество документов с отдельными изменениями для разных проектов. Поддерживать документацию для множества проектов очень тяжело, поэтому высока вероятность человеческих ошибок. Спецификация OpenAPI – это текстовый файл в формате YAML, а значит, с ним можно работать как с кодом: В наших проектах используется система контроля версий Atlassian Bitbucket c плагином Web Pages. Это позволяет одновременно работать со спецификацией, как с кодом, и видеть собранную документацию в HTML-формате. При декомпозиции какой-либо крупной задачи на набор сервисов рекомендую опираться на принцип разделения функциональности по доменам прикладных областей, а не по технологической общности. За каждым сервисом API у нас стоит определенный бизнес-процесс, и каждый сервис API описывается отдельным файлом OpenAPI-спецификации. А за него отвечает своя продуктовая команда. С учетом этого, в наборе файлов спецификаций OpenAPI каждый файл соответствует своему прикладному продукту. Поэтому остальное было делом техники: осталось завести соответствующие репозитории в Bitbucket и настроить группы ревьюеров, ответственных за каждый сервис API. В результате в Bitbucket у нас появился проект для API-спецификаций, представляющий собой общий каталог наших API. Файлы спецификаций сгруппированы по целевым продуктам: Иными словами, один репозиторий соответствует одному продукту – команде, ответственной за процессы развития и сопровождения бизнес-процессов продукта и спецификаций его API. По результатам множества выполненных проектов, структура Git-репозитория стала следующей: Кроме структуры, пришлось разработать и правила работы с репозиторием: Благодаря этим принципам мы получили удобную, прозрачную и предсказуемую среду работы со спецификациями.  При проектировании новых сервисов мы опираемся на принципы REST, но не соблюдаем их в полной мере – например, если это усложняет архитектуру сервиса или противоречит здравому смыслу.  REST – удобное отражение подхода к проектированию Domain Driven Design. На мой взгляд, благодаря REST-архитектуре у нас получаются более простые и качественные объектные модели прикладных задач, если сравнивать с тем, что мы ранее делали при помощи RPC-подходов.  Чтобы создать качественный продукт, разработчику требуется тщательно изучить предметную область задачи вне контекста опыта предыдущих проектов. Поэтому Design-first хорошо зарекомендовал себя как подход решения прикладных задач. Его можно разбить на два этапа: Изучение, описание, а также формализация сущностей и процессов предметной области. Результатом этой работы будет спецификация API сервиса, которая: Вдохновившись статьей «REST — это новый SOAP», я хотел бы поделиться практическими соображениями по теме примеров некорректного применения REST – ведь это не серебряная пуля и, надеюсь, не золотой молоток в ваших руках. Просто удивительно выглядит стремление коллег по цеху упаковать все процессы в модель Create-Read-Update-Delete. Жизнь сложнее и богаче: бизнес-процессы могут состоять из множества операций, сущности могут иметь множество состояний и переходов между ними.  Многие статьи о REST ссылаются на работу Roy Thomas Fielding «Architectural Styles and the Design of Network-based Software Architectures» как первоисточник определения REST (смотрите пятую и шестую главы этой работы). Рекомендаций использовать http- глаголы GET-POST-PUT-DELETE как единственный способ определения операций вы там не найдете.  Помните о том, что HTTP-протокол осуществляет транспортную функцию по доставке данных в запросах и ответах. Не следует смешивать уровни бизнес-логики и передачи данных. Четко разделяйте понятия «http-запрос» и «действие бизнес-процесса». HTTP-status коды предназначены для отражения состояния выполнения запроса HTTP, их не следует использовать для задач уровня бизнес-логики. Тем не менее, мы часто используем сопутствующие протоколы HTTP, которые накладывают свои обязательства на формат ответа, например: Вот некоторые типовые ситуации, в которых уместно использовать HTTP status: Отказы уровня бизнес-логики следует отражать как атрибуты сущности, над которой выполнялась операция этим HTTP-запросом.  К примеру, отказ в проведении платежа может быть обусловлен недостатком средств на счете клиента. При этом все http-запросы для проведения платежа будут выполнены успешно. Такую ситуацию следует отражать в виде атрибутов состояния сущности «Платеж». PUT-запросы не всегда подходят для операций бизнес-логики, так как они предназначены для замещения документа на сервере новым документом того же типа и структуры. GET-запрос к тому же ресурсу должен возвращать аргумент PUT-запроса.  Стандарт определяет PUT-запрос как: Пример корректного применения PUT-запроса — загрузка файла на Яндекс.Диск. RFC 5789 PATCH-запрос тоже ограничен в применимости: его семантика аналогична PUT, тело запроса представляет собой RFC 6902 JSON patch документ, а не любой документ вообще. Пример JSON patch документа: Согласитесь, описать операции бизнес-логики простым и доступным способом с таким синтаксисом будет непросто. Итак, если ваша задача удовлетворяет выше указанным требованиям, используйте PUT/PATCH, в противном случае лучше применять POST. ИТ-системы непрерывно развиваются и усложняются. Пожалуй, главным вызовом сегодняшнего дня является ограничение роста сложности систем. По моему мнению, методы правильной декомпозиции систем и управления изменениями это хорошее подспорье в нашем нелегком труде.  Надеюсь, представленный материал был вам полезен. Смотрите также мой доклад о проектировании REST-like API на JavaJam, вот ссылка на запись. ИТ Архитектор