сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · Архитектура

Дизайн API

Дизайн API

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

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

// Формулировки: «как назовёшь ресурсы?», «зачем версионирование?», «мобильному нужен один набор полей, вебу другой»

Контракт - не зеркало базы

Ресурс называют существительным, действие выражает метод: GET /orders/17, POST /orders, DELETE /orders/17. Как только в путь попадает глагол, начинается разнобой: /getOrder, /createOrder, /orderCreate, /order/new - четыре стиля в одном сервисе, и каждый новый разработчик добавляет пятый.

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

// Проверка на месте: можно ли переписать хранение с нуля, не тронув контракт? Если нет - внутреннее устройство протекло наружу, и теперь оно зацементировано.

Каждое поле - обязательство

Добавить поле в ответ безопасно: старые клиенты его просто не читают. Убрать - ломающее изменение для каждого, кто его читает, и узнаёшь ты об этом в момент выката, по звонку.

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

// Обратная крайность - отдавать заказ вместе с двумястами позициями и всей историей статусов. Если позиция весит около 300 байт, ответ тянет на 60 килобайт, из которых экран показывает три поля. На мобильной сети это лишняя секунда на ровном месте и лишняя нагрузка на базу при каждом открытии.

Что ломает клиента, а что нет

Совместимо: новое необязательное поле в ответе, новый необязательный параметр запроса, новый метод рядом со старым. Клиент этого не заметит.

Ломает: удаление поля, переименование, смена типа (число стало строкой), новое обязательное поле в запросе, сужение допустимых значений. Отдельная ловушка - новое значение перечисления: добавил статус «частично отгружен», и клиент со строгим разбором падает на незнакомом слове, хотя формально ты ничего не удалял.

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

Правила игры важнее полей

Структуру полей клиент разберёт сам по примеру ответа. Чего он не угадает: сколько запросов в минуту ему можно, что придёт при превышении, за сколько метод обязан ответить, что произойдёт при повторе.

Ограничение частоты пишут числом: «100 запросов в минуту на клиента; сверх лимита - код 429 и заголовок со временем ожидания». Без этой строчки клиент проектирует нагрузку вслепую и знакомится с лимитом в бою, в пятницу вечером.

// Вторая обязательная строчка - идемпотентность для операций, меняющих состояние. Клиент отправил создание платежа, ответ потерялся в сети, клиент повторил запрос. Без ключа идемпотентности человек заплатит дважды, и разбирать это будет поддержка.

идемпотентность
повтор того же запроса с тем же ключом даёт тот же результат и не создаёт вторую сущность

Как отвечать: «Клиенту нужны данные из трёх сервисов на одном экране. Что предложишь?»

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

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

На чём валятся

  • − Проектируют контракт зеркалом схемы базы и цементируют хранение.
  • − Публикуют поля «на будущее», которые потом нельзя убрать.
  • − Вкладывают в ответ все связанные сущности и раздувают его в десятки килобайт.
  • − Считают новое значение перечисления безопасным изменением.
  • − Заводят отдельный метод под каждый экран приложения.
  • − Не описывают лимиты, таймауты и идемпотентность - клиент узнаёт о них в бою.

Проверьте себя

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

  1. #ana_arc_api_design1 / 5
    Что стоит вынести в контракт, а что оставить внутренней деталью сервиса?
    A)Всё внутреннее устройство — для прозрачности
    B)Только то, что нужно потребителю
    C)Всё, что может понадобиться в будущем
    D)Ровно те поля, что есть в таблицах
    показать ответ и разбор
    +B)Только то, что нужно потребителю

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

  2. #ana_arc_api_design2 / 5
    Метод возвращает заказ вместе с полным списком позиций и историей статусов. В чём риск?
    A)Ответ распухает и тянет лишние данные
    B)Клиент не сможет разобрать вложенность
    C)Нарушается принцип именования ресурсов
    D)Метод перестанет быть идемпотентным
    показать ответ и разбор
    +A)Ответ распухает и тянет лишние данные

    // разбор: У заказа могут быть сотни позиций и десятки смен статуса, а списку заказов это всё не нужно. Ответ раздувается, база нагружается, а клиент выбрасывает большую часть. Обычное решение — отдавать вложенное отдельными ресурсами либо позволять клиенту явно запрашивать нужный состав.

  3. #ana_arc_api_design3 / 5
    Что описать в контракте, кроме структуры запроса и ответа?
    A)Язык реализации сервиса
    B)Схему таблиц внутри сервиса
    C)Число экземпляров сервиса
    D)Ограничения по частоте вызовов
    показать ответ и разбор
    +D)Ограничения по частоте вызовов

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

  4. #ana_arc_api_design4 / 5
    Мобильному клиенту нужен один набор полей, веб-интерфейсу — другой. Что предложить?
    A)Отдавать всем максимально полный ответ
    B)Завести два разных метода на каждый экран
    C)Дать параметр выбора состава ответа
    D)Хранить предпочтения клиента на сервере
    показать ответ и разбор
    +C)Дать параметр выбора состава ответа

    // разбор: Явный параметр состава решает задачу, не плодя методы: клиент просит то, что ему нужно. Альтернатива — отдельный слой агрегации под каждый тип клиента. Метод на каждый экран плох тем, что интерфейс начинает повторять устройство приложения и множится с каждым новым макетом.

  5. #ana_arc_api_design5 / 5
    Почему интерфейс, повторяющий структуру базы, считают плохим решением?
    A)Он работает медленнее прочих
    B)Любое изменение хранения ломает клиентов
    C)Его труднее описать спецификацией
    D)Он не поддерживает версионирование
    показать ответ и разбор
    +B)Любое изменение хранения ломает клиентов

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

дальше

Теорию прочитали. Навык ставится повторением

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