Другое

Полиморфные JSON-объекты: Лучший подход к API

Узнайте, почему полиморфные JSON-объекты предпочтительнее плоских с null, и как реализовать ответы, улучшая клиентский опыт и поддержку для масштабируемых систем.

Нормально ли отправлять JSON‑ответ, содержащий список объектов, свойства которых зависят от типа сущности? Например:

json
{
    "entity_type": "company",
    "registered_date": "2020-01-01",
    "reference_number": "ABC1234567",
    "legal_form": "partnership"
},
{
    "entity_type": "person",
    "date_of_birth": "1990-01-01",
    "reference_number": "12345",
    "age": 35
}

Считается ли это хорошим дизайном API, или лучше вернуть один плоский объект со всеми возможными полями, оставляя нерелевантные как null?

Полиморфные объекты в ответах JSON‑API действительно являются распространённой и принятым практикой, особенно когда речь идёт о сущностях с фундаментально разными свойствами. В вашем примере показан устоявшийся шаблон, где объекты имеют общие поля, но также и тип‑специфические свойства. Такой подход обычно предпочтительнее, чем возвращать один плоский объект с множеством null‑значений, поскольку он обеспечивает более чистую и осмысленную структуру данных, которую легче обрабатывать на стороне клиента.

Содержание

Понимание полиморфных JSON‑объектов

Полиморфные JSON‑объекты, как ваш пример с сущностями «компания» и «человек», являются естественным продолжением концепций объектно‑ориентированного программирования в JSON‑API. Такой подход позволяет разным типам объектов совместно использовать общий интерфейс, при этом сохраняя свои уникальные свойства.

Почему полиморфные объекты распространены:

  • В @balanced есть конечная точка, которая возвращает полиморфные типы Credit, Debit, Hold, Refund и Reversal в порядке создания GitHub json‑api issue
  • Этот шаблон позволяет API эффективно обрабатывать коллекции связанных, но отличных сущностей
  • Клиент может корректно обрабатывать различные типы сущностей, опираясь на поле entity_type

Ключевые характеристики полиморфного JSON:

  • Каждый объект содержит идентификатор типа (ваше поле entity_type)
  • Общие свойства разделяются между типами (например, reference_number)
  • Тип‑специфические свойства включаются только для соответствующих объектов
  • Структура остаётся предсказуемой и валидируемой

«Полиморфизм и наследование – это ключевые паттерны, которые улучшают дизайн вашего API и избавляют от дублирования кода» – Bump.sh API documentation guide

Плоские объекты с null: альтернативный подход

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

Рассмотрение плоских объектов:

  • Reddit‑обсуждение указывает, что вложенные объекты дают лучший контекст для соответствующих свойств r/AskProgramming
  • Сжатие JSON создаёт компромисс между компактностью и производительностью чтения Stack Overflow
  • Ведутся дискуссии о том, должны ли JSON‑объекты явно включать null‑значения Stack Overflow

Потенциальные проблемы плоских объектов:

  • Неоднозначность: отсутствие свойств создаёт неясность, является ли поле null или просто отсутствует
  • Путаница типов: свойства, не применимые к определённым типам сущностей, становятся бессмысленными
  • Сложность схемы: схема становится громоздкой из-за полей, которые могут никогда не использоваться
  • Сложность на стороне клиента: клиентам приходится постоянно проверять null‑значения и обрабатывать условную логику

Сравнение обоих подходов

Давайте рассмотрим ключевые различия между полиморфным и плоским подходами:

Аспект Полиморфные объекты Плоские объекты с null
Читаемость Высокая – каждый объект содержит только релевантные данные Низкая – множество null/неактуальных свойств
Обработка клиентом Простая – обрабатывается по типу сущности Сложная – условная логика для каждого свойства
Документация API Чёткое разделение ответственности Единая, но потенциально запутанная схема
Эффективность данных Эффективная – передаётся только необходимая информация Потенциально неэффективная – много null‑значений
Валидация Тип‑специфические схемы можно валидировать Одна схема с множеством опциональных полей
Расширяемость Лёгко добавлять новые типы сущностей Труднее – затрагивает всех существующих клиентов

Примеры из реального мира:

Обсуждение на Stack Overflow о моделировании RESTful API с наследованием демонстрирует именно этот паттерн в OpenAPI/Swagger:

yaml
requestBody:
  content:
    application/json:
      schema:
        oneOf:
          - $ref: '#/components/schemas/Dog'
          - $ref: '#/components/schemas/Cat'
          - $ref: '#/components/schemas/Fish'
        discriminator:
          propertyName: animal_type

Это показывает, что полиморфизм не только теоретический, но и активно поддерживается современными стандартами документации API.

Лучшие практики реализации

Для полиморфных объектов:

  1. Используйте единый идентификатор типа: ваше поле entity_type идеально подходит для этого
  2. Тщательно документируйте схему: применяйте ключевые слова oneOf и discriminator в OpenAPI 3.0
  3. Обеспечьте обратную совместимость: при добавлении новых типов существующие клиенты должны продолжать работать
  4. Проводите валидацию с обеих сторон: убедитесь в серверной валидации и корректной обработке типов на клиенте

Исследовательские выводы:

  • «Используйте явное поле в объекте, чтобы указать его тип. Большинство библиотек для маппинга JSON в объекты можно настроить так, чтобы они создавали объекты соответствующих подклассов» – Software Engineering Stack Exchange

Обработка полиморфных ответов:

  • Salesforce Stack Exchange предлагает, что для полиморфных ответов может потребоваться парсер, чтобы эффективно обрабатывать разные типы данных Salesforce Stack Exchange

Учитывайте десериализацию:

  • В .NET «Эта конфигурация позволяет полиморфную сериализацию для WeatherForecastBase, особенно когда тип во время выполнения – WeatherForecastWithCity» – Microsoft Learn

Стандарты отрасли и документация

Поддержка OpenAPI/Swagger:

  • «OpenAPI 3.0 (ранее Swagger) действительно поддерживает полиморфизм в JSON» – Stack Overflow
  • Решение для представления полиморфных объектов в JSON Schema – «переопределить свойство type в схемах then» – Stack Overflow

Стандарт JSON API:

  • Сообщество JSON API обсуждает именно этот сценарий: «Допустим, у меня есть ресурс Transportation на /transportations, а Transportation может иметь различные типы» – JSON API discuss

Опыт разработки:

  • Как отметил один Java‑разработчик, «десериализация ответа сервиса оказалась в коде, который ничем не читаем, потому что нет прямой связи с классами через геттеры/сеттеры/конструкторы» при использовании плоского JSON для иерархических данных Software Engineering Stack Exchange

Выводы и рекомендации

На основании исследовательских выводов полиморфные JSON‑объекты не только являются нормальной практикой, но и считаются лучшим подходом для API, работающих с сущностями разного типа.

Ключевые выводы:

  1. Полиморфные объекты – стандарт: этот паттерн широко используется и поддерживается основными стандартами API, такими как OpenAPI 3.0
  2. Лучше для клиентского опыта: клиенты получают только релевантные данные и могут корректно обрабатывать разные типы сущностей
  3. Более поддерживаемый: добавление новых типов не ломает существующий клиентский код
  4. Согласие отрасли: множество источников рекомендуют полиморфизм вместо плоских объектов с null

Рекомендации:

  • Продолжайте использовать полиморфные объекты: ваш текущий подход соответствует лучшим практикам
  • Тщательно документируйте: применяйте возможности OpenAPI 3.0, такие как oneOf и discriminator, чтобы чётко определить полиморфное поведение
  • Учитывайте потребности клиентов: убедитесь, что ваши клиентские библиотеки могут эффективно обрабатывать полиморфные ответы
  • Планируйте расширяемость: проектируйте API так, чтобы легко добавлять новые типы сущностей в будущем

Исследования ясно показывают, что полиморфизм в JSON‑API – «ключевой паттерн, который улучшает дизайн вашего API и избавляет от дублирования кода», а не редкое исключение. Ваш пример демонстрирует этот паттерн на практике, делая его как нормальным, так и рекомендуемым для хорошего дизайна API.

Авторы
Проверено модерацией
Модерация