Статья от фронтенд-одноклассников, жалующихся на бэкенд-интерфейс

API

предисловие

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

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

Кто должен вести дизайн интерфейса

Или, говоря более прямо, дизайн интерфейса должен решать потребитель или поставщик интерфейса?

Конечно потребитель интерфейса

Самое парадоксальное в «интерфейсе» то, что провайдер приложил немало усилий для его реализации, но он (почти) больше никогда не используется. Так что очень легко попасть в самовозвеличивающую ситуацию, потому что не знает качества интерфейса. Это похоже на повара, который никогда не пробует свои собственные блюда: вы ожидаете, что его кулинария будет такой же хорошей, как и его кулинарные способности. Неявная предпосылка вышеизложенного (я думаю) состоит в том, что есть абсолютно хорошие и плохие интерфейсы.Плохие интерфейсы неудобны для звонков потребителям, трудны для обслуживания провайдерами, а также приводят к неудобному поведению продукта и плохому опыту.

Но какое отношение качество интерфейса имеет к тому, кто руководит дизайном? Потому что одна из причин плохого интерфейса в том, что провайдер решает проблему только с точки зрения разработчика:


Пример 1 (Chatty API)

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

  1. Создайте пустую информационную панель с основной информацией, заполненной пользователем, а также с размером и положением диаграммы.
  2. Затем заполните информационную панель конкретной информацией о диаграмме, такой как тип диаграммы, используемые измерения и индикаторы и т. д.

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

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

Back-end R&D может утверждать, что микросервисы используются в back-end, а разные типы данных хранятся в разных сервисах, поэтому вам нужно взаимодействовать с разными сервисами для достижения полного хранения. Чего они никогда не понимают, так это того, что реализация бэкенда приводит к фрагментации интерфейса, и это ваша проблема, не стоит перекладывать эту часть нагрузки на фронтенд-разработчиков, на самом деле она тоже косвенно перекладывается пользователям. Не запугивайте меня тем, что я не знаю бэкенд, по крайней мере, я понимаю, что добавление слоя Orchestration Layer, похожего на BFF, может решить эту проблему.

Инженер Netflix Дэниел Джейкобсон в своей статьеThe future of API design: The orchestration layerВ нем указано, что API — это не что иное, как два типа аудитории:

  1. LSUD: Large set of unknown developers
  2. SSKD: Small set of known developers

С тенденцией обслуживания продуктов весьма вероятно, что необходимо предоставить интерфейс публичным разработчикам, то есть LSUD, таким как AWS или Github. Не говоря уже о том, что интерфейсное решение в приведенном выше примере будет затоплено звездами плевка, очень опасно так явно выставлять напоказ детали внутренних сервисов.

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

Использование внутреннего мышления для разработки интерфейсов отражается не только в дизайне URI, но и в структуре параметров запроса и тела возврата:


Пример 2

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

В идеале мы ожидаем, что возвращаемые данные будут в единицах статей, например

articles: [
	{
  		id: ,
        author: {},
        comments: []
	},
    {
    	id:
        author: {},
        comments: []
    }
]

Однако результат, возвращаемый серверной частью, может быть в единицах сущностей:

{
    articles: [],
    authors: [],
    comments: []
}

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

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

О метаинформации


Пример третий:

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

{
    meta: {
      code: 0,
      error: null,
      host: "127.0.0.1"
    },
   	result: []
}

На мой взгляд, есть две проблемы с приведенной выше структурой данных.

метаинформация содержит независимую информацию о состоянии

В дизайне интерфейса метаинформации, включая код состояния, скрытая логика по умолчанию такова: код состояния HTTP, возвращаемый интерфейсом, должен быть 200, и действительно ли данные успешно получены, необходимо судить по пользовательскому коду кода состояния в мета (другими словами, интерфейсы, которые вы видите выше, на самом деле являются «интерфейсами интерфейсов»). В конце концов, во внешнем коде не нужно судить, нормальный возврат или нет через HTTP-код, а нужно судить только о возврате в интерфейсе.meta.codeТолько что

**Но кто дал вам уверенность в том, что внутренний интерфейс не будет зависать? ! ** Независимо от того, как серверная часть обеспечивает надежность интерфейса, передняя часть все равно должна сначала определить, является ли код HTTP 200, а затем оценитьmeta.codeкак и ожидалось. Дело не в доверии, а в надежности моей программы.

Поскольку интерфейс все равно приходится оценивать дважды, почему бы и нетmeta.codeВ сочетании с HTTP-кодом? Более того, мне также необходимо локально поддерживать значение перечисления пользовательского кода, а также обеспечивать синхронизацию с серверной частью. Это приводит к следующему вопросу:

Где хранить метаинформацию

Нет ничего плохого в том, что нам нужна метаинформация, но метаинформация нам не так уж нужна. Это выражается в нескольких моментах:

  1. Действительно ли нам нужно поле для отображения метаинформации параллельно возвращаемому результату?
  2. Нужна ли нам метаинформация для каждого запроса?
  3. Должна ли метаинформация находиться в метаполе?

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

Что касается того, где находится метаинформация, я обычно включаю ее в заголовок HTTP. Напримерmeta.codeВместо этого вполне возможно использовать HTTP-код, я не вижу смысла всегда гарантировать возврат 200 и собственный код.

Что касается другой метаинформации, вы можете передатьX-Пользовательский HTTP-заголовок в начале передается. Например

Информация об ограничениях использования в Github APIПросто поместите его в заголовок HTTP:

Status: 200 OK
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4999
X-RateLimit-Reset: 1372700873

Design for today


Пример четвертый

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

{
    data: [
        {
            date: "2019-06-08",
            result: [
                
            ]
        }
    ]
}

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

Это может показаться умным решением: «Смотрите, я предусмотрительно предусмотрел будущие требования!», но на самом деле это довольно глупо: вы выполняете требование, но это требование, которого сейчас нет и не обязательно существует. будущие требования, и если вы действительно хотите писать код, ориентированный на будущее, вам предстоит реализовать тысячи будущих требований.

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

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

Кто-то может сказать, что это не просто дополнительный слой инкапсуляции? Не нужно много усилий, чтобы реализовать то, что суета. Извините, я не беру в расчет этот случай, но подчеркиваю, что ни в коем случае нельзя добавлять бессмысленный код, как бы сложно его ни было реализовать.

«Сосредоточьтесь на настоящем» имеет еще одно измерение:


Пример пятый

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

Пример 6

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


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

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

В примере 6, хотя мы и можем добиться достигнутого эффекта, это нельзя расценивать как повторное использование интерфейса, а только как взлом интерфейса (разница между взломом и повторным использованием заключается в том, использовать ли исходную функцию элемента делать дела). И интерфейс взлома рискован. Для поставщика интерфейса они больше обеспокоены «ортодоксальными» потребителями услуги интерфейса. В этом случае существование интерфейса должно отображать полную информацию о статье. Если однажды «информация о статье «Это изменение требований может привести к одновременному изменению информации об авторе, сокращению или даже отмене полей. Тогда они не обязаны этим взламывать пользователей.Интерфейс должен фокусироваться на чем-то одном

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

Не ограничивается REST API

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

Например, в случае данных в реальном времени, теоретически, серверная часть (при обновлении данных) управляет обновлением внешнего вида, что должно быть операцией push. Но в традиционной реализации нам все равно приходится реализовывать функцию через пассивное ожидание и опрос.

Использование WebSocket или Streaming кажется лучшим выбором для требований к типам, управляемым событиями. Если это взаимодействие между бэкендами, также можно использовать WebHook. Обычно у меня есть оговорки в отношении новых технологий, но я должен признать, что GraphQL также может обрабатывать определенные требования лучше, чем REST API. И поддержка интерфейса GraphQL большинством поставщиков предполагает, что это возможно.

Я понимаю, что реализация API — это лишь малая часть функции реализации бэкенда, за интерфейсом скрываются дополнительные модификации бизнес-логики и изменения в структуре библиотечных таблиц. Говорят даже, что половина интерфейсной части отдается на реализацию фреймворку. Однако, даже если есть только небольшой шанс, эта ссылка должна быть сделана до совершенства.

заключительные замечания

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

Back-end студенты, если вам интересно сделать интерфейс лучше, послушайте отзывы "потребителей". Если вы пытались разрабатывать приложения с использованием сторонних интерфейсов, таких как Slack и Github, вы обнаружите, что их интерфейсы постоянно обновляются. Постоянно происходит отказ от старых интерфейсов и внедрение новых. Этот вид итерации не праздный, а вне голоса и потребностей реальных пользователей.

Наконец, я рекомендую книгу, которую я недавно прочитал о дизайне API, которая очень полезна:

  • Дизайн и разработка веб-API
  • Designing Web APIs
  • APIs A Strategy Guide

Эта статья также была опубликована в моемЗнай колонку, приветствую всех, чтобы обратить внимание