А сколько у вас в компании во внутренних системах используется наименований одного и того же поля в API? А сколько способов назвать поле, которое перечисляет список id?
Я часто сталкиваюсь с тем, что при проектировании и разработке HTTP REST API команды (чаще неосознанно) собирают целый семантический и лексический зоопарк наименований. Потом бывает сложно разобраться, что нужно записать в определенное поле, или какое название поля выбрать для перечисления списка ID (например) из уже существующих.
При проектировании и разработке HTTP REST API, консистентность в именовании параметров и ресурсов является недооценненным (по моему мнению) аспектом, который влияет на понятность и удобство использования API.
Консистентность (или согласованность) означает использование одинаковых, похожих и понятных обозначений для свойств, методов и других элементов системы. Это помогает уменьшить когнитивную нагрузку:
-
при проектировании: нет необходимости каждый раз изобретать названия;
-
при чтении: стандартные свойства несут одинаковое значение в разных методах;
-
при реализации: меньше возможность опечатки, т. к. IDE (среда разработки) поможет выбирать из уже существующих переменных.
Поэтому я однажды собрала для себя и коллег гайд–чек-лист под названием “Приглаживаем названия в API” и теперь публикую его для широкой аудитории. Уверена, что он кому-то да пригодится.
Быстрый чек-лист для проверки консистентности:
А теперь поподробнее.
1. Свойства написаны в едином стиле
Существуют разные практики грамматического оформления свойств:
-
camelCase
, например"createdAt": 1320296464
; -
snake_case
, например"created_at": "Thu Nov 03 05:19;38 +0000 2011"
; -
PascalCase
, например"DateTime": "2011-10-29T09:35:00Z"
; -
kebab-case
, например"date-time": "2011-10-29T09:35:00Z"
; -
UPPER_CASE_SNAKE_CASE
, например"UPDATE_TIME": "2011-10-29T09:35:00Z"
; -
и другие вариации.
Часто на разных слоях системы придерживаются разных стилей, но в пределах одного слоя стоит использовать один (с некоторыми оговорками) стиль.
С разработчиками из моей команды у нас такая договоренность:
-
В БД мы используем snake_case.
-
В API мы используем camelCase.
2. Используются стандартные названия и стандартные правила наименования
В большинстве систем используются одни и те же стандартные свойства, например:
-
Пагинация
-
limit, offset
(Facebook) -
page, rpp (records per page)
(Twitter) -
pageNumber, pageSize
-
start, count
(LinkedIn)
-
-
Текстовый поиск
-
q, subString, search, text
-
-
Диапазоны
-
startDate, endDate
-
startAt, stopAt
-
depart_start, depart_range
(количество дней +- от даты)(aviasales) -
price_min, price_max
-
-
Поля в ответе
-
fields
-
-
Сортировка
-
sort, sortBy, sortProperty
— признак сортировки -
order, sortDirection
— направление сортировки -
sort=-age
— два в одном -
Многоуровневая сортировка:
sort[name]=ASC, sort[email]=DESC
-
-
И много других
Составьте свой словарь стандартных названий и переиспользуйте те же свойства
Составьте правило формирования названий. Например, если вы возвращаете дату и время, используйте постфикс smthAt. Если указываете проценты, используйте постфикс smthPct.
*Постфикс и префикс могут быть любые, просто используйте релевантные и одинаковые для решения похожих задач.
Если не придерживаться справочника, в одном ответе JSON будет свойство pctPrice
, в другом pricePct
, рядом еще createdDate
и createdAt
и будет сложно и проектировать, и разрабатывать, и авто-тесты писать.
3. Названия в единственном и множественном числах согласованы
Сразу пример для иллюстрации:
-
Идентификатор пользователя передается как
userId
, а список идентификаторов какuserList
Как сказал один из системных аналитиков команды: «Выглядит очень загадочно«.
Я предпочитаю использовать конструкции, в которых можно найти общий знаменатель. Например такие пары:
Идентификатор пользователя —
userId
, а список идентификаторов —userIds
Аналогично, например,
errorCode
, а список —errorCodes
Менее предпочтительные варианты, но тоже имеют место быть:
-
Идентификатор пользователя —
user
, а список идентификаторов —userList
Однако, смущает, что по названию не понятно, идентификатор это или целый объект с данными (см. п.5 чек-листа) -
Идентификатор пользователя —
user_id
, а список идентификаторов —user_id_list
.
Конструкция сложная и длинная, но ее можно использовать, если самый первый вариант не подходит для всех комбинаций, которые нужны.
4. Названия согласованы между несколькими слоями приложения
Чаще всего атрибуты в таблице сущности и в ее отражении в API схожи.
Например, если в таблице есть поле discount_pct
(скидка в процентах), логично соответствующее свойство в API назвать discountPct
. Это упростит жизнь и аналитику, и тестировщику, и разработчику, и команде поддержки.
5. По названию поля понятно, что в нем содержится
Если без технической документации вам не понятно, что нужно вписать в поле, или что конкретно вы получите в ответе — стоит его поменять. Плохие примеры:
[{ "request": "N12345", "author": "Anna" }]
-
По полю
author
не понятно, что в ответе будет имя. Я бы назвала свойствоauthorName
, а еще лучше сделала бы вложенный объектauthor
со свойствамиid, name
GET /recommendations?code=1234
-
В этом случае поле
code
слишком абстрактное. Это пример из практики. В результате оказалось, что там нужно указать тип рекомендации.
6. По названию поля понятно, как оно используется
Этот пункт тесно связан с предыдущим, но немного о другом. Приведу пример из реального драфта технической спецификации, которую я изучала:
{ "taskId": "1", "atLeastOne": true, "channelIds": ["1", "2", "3", "4"] }
Логика: Если
atLeastOne = false
, проверка считается финальной и результат нужно сохранить в БД.
Уже видите количество часов, которое понадобится для того, чтобы разгадать эту особенность без технической спецификации?
Контекст для тех, кто хочет разобраться
Контекст был такой: нужно проводить предварительную проверку («просто посмотреть») и финальную (зафиксировать результат). Так совпало по бизнес-требованиям, что для предпроверки нужно было проверять в режиме «хотя бы одно верно», а для финальной проверки «все верно«. Логика при проектировании так и сложилась:
-
если режим не «хотя бы одно верно» (
atLeastOne = false
) => значит проверка финальная => значит сохраняем в БД
В чем проблема: Поле atLeastOne
по смыслу и названию означает «хотя бы один или все», а применяется как «предпроверка или финальная проверка» или «сохрани или не сохрани».
Что стоило бы сделать:
-
пусть поле
atLeastOne
управляет логикой проверки и говорит: «хотя бы один или все» -
пусть доп. поле
isFinal
управляет режимом проверки и говорит: «предпроверка или финальная проверка» -
или доп. поле
saveResult
управляет результатом проверки и говорит: «сохрани или не сохрани»
Делайте так, чтобы ваши поля говорили сами за себя.
7. Отсутствует полиморфизм в структуре
Что такое полиморфизм? Вернемся к одному из предыдущих примеров. Например, содержимое ответа на запрос GET /requests
ответ выглядит так:
{ "request": "1", "author": "Anna" }
А ответ на запрос GET /requests/1
так:
{ "request": "1", "author": { "id": "123", "name": "Anna" } }
Это полиморфизм структуры ресурса (в данном случае вложенного):
в одном случае поле
author
— это строка с именем;в другом случае поле
author
— это объект с вложенными данными.Такой ситуации стоит избегать. Если один и тот же объект используется во многих эндпоинтах, я рекомендую его сразу заключать в объект, как в
GET /titles/1
Иногда так делают, чтобы просто не возвращать все данные, укоротить ответ. Но в таком случае стоит отдельным параметром (да или ладно, просто в коде) управлять списком полей в ответе (если не во всех ответах требуются все из них).
Подробнее про объекты
P. S. Я бы хотела, чтобы эта статья дополнялась и была живой, поэтому если у вас есть свои стандарты и предложения по наименованиям и правилам, оставляйте их в комментариях, будем вместе пополнять рекомендации.
ссылка на оригинал статьи https://habr.com/ru/articles/860010/
Добавить комментарий