[Документация Yandex Cloud](../../index.md) > [Yandex MetaData Hub](../index.md) > Schema Registry > Концепции > Контентные модели и проблемы эволюции JSON-схем

# Контентные модели и проблемы эволюции JSON-схем

Yandex Schema Registry поддерживает работу со схемами [Avro](https://avro.apache.org/), [Protobuf](https://protobuf.dev/) и [JSON Schema](https://json-schema.org/). Схемы Avro и Protobuf основываются на заранее определенной структуре, которая строго поддерживается и регулирует эволюцию схем. В отличие от них, JSON-схема не требует заранее определенной структуры, поэтому не имеет строго описанных правил эволюции схемы. Из-за этого поведение JSON-схемы при работе с опциональными параметрами отличается от поведения схем Avro и Protobuf. 

> Например, добавление опционального параметра в схему Avro или Protobuf считается совместимым изменением схемы, а в JSON-схеме это же действие может как нарушить, так и не нарушить совместимость схем, в зависимости от используемой [контентной модели](#content-models).

Чтобы сохранить совместимость при эволюции JSON-схем, применяются проверки совместимости, правила которых задаются [политиками проверки совместимости](compatibility-check-policy.md). В Schema Registry реализованы следующие политики для JSON-схем:

* Confluent — не позволяет добавлять и удалять опциональные параметры в полях типа `object`. Иногда это ограничение может быть избыточным, например при работе с очередями.

* Optional Friendly — позволяет добавлять и удалять опциональные параметры в полях типа `object`, сохраняя полную транзитивную совместимость.

В этой статье описываются теоретические основы и реализация политики `Optional-friendly`.

{% note tip %}

Если вы хотите использовать в [Data Transfer](../../data-transfer/concepts/index.md) JSON-схему с политикой проверки совместимости `Optional-friendly`:

1. [Задайте в пространстве имен](../operations/update-name-space.md) политику проверки совместимости `Optional-friendly`.
1. [Задайте в настройках сериализации эндпоинта-приемника](../../data-transfer/operations/endpoint/target/kafka.md#serializer) Yandex Managed Service for Apache Kafka® следующие настройки Debezium:

    * Для ключа схемы: `key.converter.dt.json.generate.closed.content.schema` — `true`.
    * Для значения схемы: `value.converter.dt.json.generate.closed.content.schema` — `true`.

{% endnote %}

## Контентные модели JSON-схем {#content-models}

_Контентная модель_ — это совокупность правил, по которым может изменяться поле типа `object` в схеме. У разных полей в одной схеме могут быть разные контентные модели. 

В JSON Schema существуют следующие контентные модели:

* [открытая контентная модель](#open-model);
* [закрытая контентная модель](#closed-model);
* [частично открытая контентная модель](#partially-open-model).

### Открытая контентная модель {#open-model}

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

Описание схемы в открытой контентной модели выглядит так:

```json
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "age": {"type": "integer"}
  },
  "additionalProperties": true
}
```

Где `additionalProperties` — возможность добавления параметров, `true` или `false`. По умолчанию параметр имеет значение `true`, поэтому при описании схем в открытой контентной модели он часто опускается.

### Закрытая контентная модель {#closed-model}

В закрытой контентной модели можно использовать только параметры, заданные в схеме. Добавлять новые параметры нельзя.

Описание схемы в закрытой контентной модели выглядит так:

```json
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "age": {"type": "integer"}
  },
  "additionalProperties": false
}
```

### Частично открытая контентная модель {#partially-open-model}

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

* [Разрешить добавление параметров только определенного типа](#property-type-limitation), указав тип параметра в `additionalProperties`.
* [Разрешить добавление параметров определенных типов с именем заданного вида](#pattern-based-limitation), перечислив регулярные выражения для имен и соответствующие им типы параметров в объекте `patternProperties`.

##### **Ограничение по типу добавляемого параметра** {#property-type-limitation}

   Допустимые типы новых параметров описываются в параметре `additionalProperties`. Добавлять параметры другого типа нельзя. 

   Описание схемы выглядит так:

   ```json
   {
     "type": "object",
     "properties": {
       "name": {"type": "string"},
       "age": {"type": "integer"}
     },
     "additionalProperties": {"type": "string"}
   }
   ```

   Эта схема разрешает добавлять только новые параметры типа `string`.

##### **Ограничение по типу добавляемого параметра на основе префикса имени параметра** {#pattern-based-limitation}

   Параметр `additionalProperties` имеет значение `false`, как в закрытой схеме. Допустимые новые параметры описываются в объекте `patternProperties`, при этом имени каждого параметра присваивается префикс, указывающий на тип параметра. Имена параметров в схеме описываются регулярными выражениями. Нельзя добавлять параметры, не описанные в `patternProperties`.

   Описание схемы выглядит так:

   ```json
   {
     "type": "object",
     "properties": {
       "s_name": {"type": "string"},
       "i_age": {"type": "integer"}
     },
     "patternProperties": {
       "^s_": {"type": "string"},
       "^i_": {"type": "integer"}
     },
     "additionalProperties": false
   }
   ```

   Эта схема разрешает добавлять только:

   * Параметры типа `string`, имена которых начинаются с `s_`.
   * Параметры типа `integer`, имена которых начинаются с `i_`.

## Особенности эволюции JSON-схем в контентных моделях {#schema-evolution-considerations}

_Эволюция схемы_ — это изменение одного или нескольких параметров схемы или их свойств. В JSON-схеме возможны следующие изменения:

* добавление нового обязательного параметра;
* добавление нового опционального параметра;
* удаление обязательного параметра;
* удаление опционального параметра;
* изменение типа параметра с опционального на обязательный;
* изменение типа параметра с обязательного на опциональный.

Открытая и закрытая контентные модели по-разному ограничивают возможности эволюции схемы при сохранении совместимости с предыдущими схемами. Конкретные ограничения зависят от модели.

### Открытая контентная модель {#open-model-compatibility-chart}

   | Изменение схемы                                   | Прямая совместимость | Обратная совместимость | Полная совместимость |
   | ------------------------------------------------- | :------------------: | :--------------------: | :------------------: |
   | Добавление обязательного параметра                | ![image](../../_assets/common/yes.svg) | ![image](../../_assets/common/no.svg) | ![image](../../_assets/common/no.svg) |
   | Добавление опционального параметра                | ![image](../../_assets/common/yes.svg) | ![image](../../_assets/common/no.svg) | ![image](../../_assets/common/no.svg) |
   | Удаление обязательного параметра                  | ![image](../../_assets/common/no.svg) |  ![image](../../_assets/common/yes.svg) | ![image](../../_assets/common/no.svg) |
   | Удаление опционального параметра                  | ![image](../../_assets/common/no.svg) | ![image](../../_assets/common/yes.svg) | ![image](../../_assets/common/no.svg) |
   | Изменение опционального параметра на обязательный | ![image](../../_assets/common/yes.svg) | ![image](../../_assets/common/no.svg) | ![image](../../_assets/common/no.svg) |
   | Изменение обязательного параметра на опциональный | ![image](../../_assets/common/no.svg) | ![image](../../_assets/common/yes.svg) | ![image](../../_assets/common/no.svg) |

### Закрытая контентная модель {#closed-model-compatibility-chart}

   | Изменение схемы                                   | Прямая совместимость | Обратная совместимость | Полная совместимость |
   | ------------------------------------------------- | :------------------: | :--------------------: | :------------------: |
   | Добавление обязательного параметра                | ![image](../../_assets/common/no.svg) | ![image](../../_assets/common/no.svg) | ![image](../../_assets/common/no.svg) |
   | Добавление опционального параметра                | ![image](../../_assets/common/no.svg) | ![image](../../_assets/common/yes.svg) | ![image](../../_assets/common/no.svg) |
   | Удаление обязательного параметра                  | ![image](../../_assets/common/no.svg) | ![image](../../_assets/common/no.svg) | ![image](../../_assets/common/no.svg) |
   | Удаление опционального параметра                  | ![image](../../_assets/common/yes.svg) | ![image](../../_assets/common/no.svg) | ![image](../../_assets/common/no.svg) |
   | Изменение опционального параметра на обязательный | ![image](../../_assets/common/yes.svg) | ![image](../../_assets/common/no.svg) | ![image](../../_assets/common/no.svg) |
   | Изменение обязательного параметра на опциональный | ![image](../../_assets/common/no.svg) | ![image](../../_assets/common/yes.svg) | ![image](../../_assets/common/no.svg) |

### Частично открытая контентная модель {#partially-open-model-compatibility-issues}

Частично открытая контентная модель позволяет добавлять новые параметры с сохранением совместимости, указывая тип параметра в `additionalProperties`. Однако таким способом можно добавлять в модель только параметры одного типа.

Чтобы добавить параметры сразу нескольких типов, перечислите в `patternProperties` типы параметров, присвоив именам параметров соответствующий префикс. Необходимо описать все возможные параметры, включая объекты `object` и массивы `array`.

Такая реализация имеет следующие недостатки:

* Схема становится громоздкой и трудноподдерживаемой.
* Возможную ошибку в `patternProperties` нельзя исправить без потери совместимости.
* Нельзя использовать новые типы параметров, если такие будут добавлены в спецификацию JSON, потому что они не описаны в `patternProperties`.
* Схема работает, только если имена всех параметров строго соответствуют описанным шаблонам. Это возможно, только если вы контролируете производителя данных.

## Сохранение совместимости при эволюции опциональных параметров {#optional-parameters-compatibility-solution}

Используя открытые и закрытые контентные модели JSON-схем, невозможно обеспечить [полную совместимость](#open-model-compatibility-chart) при добавлении или удалении опциональных параметров. Это приводит к возникновению ошибок `PROPERTY_ADDED_TO_OPEN_CONTENT_MODEL` и `PROPERTY_REMOVED_FROM_CLOSED_CONTENT_MODEL` при операциях с опциональными параметрами. Частично открытая контентная модель обеспечивает совместимость в этих случаях, но [имеет существенные недостатки](#partially-open-model-compatibility-issues).

Альтернатива применению конкретной модели — использовать разные контентные модели для производителей и потребителей данных:

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

При этом в реестре схем регистрируется только схема данных производителя. Схема для потребителя генерируется по необходимости на основе схемы производителя, но не регистрируется в реестре. Единственным отличием схем производителя и потребителя становится использование разных контентных моделей. Это сводит проверку совместимости схем к конвертации схемы потребителя из открытой контентной модели в закрытую и проверке схем в реестре производителя. Если аналогичная схема зарегистрирована в реестре для производителя, то проверка на совместимость пройдена. В то же время схема потребителя может содержать дополнительные параметры из других зарегистрированных схем производителя. Для поддержки полной транзитивной совместимости требуется, чтобы схема потребителя была совместима хотя бы с одной зарегистрированной схемой производителя. Подробнее о [теоретическом обосновании](https://www.creekservice.org/articles/2024/01/08/json-schema-evolution-part-1.html) и [прикладных аспектах](https://www.creekservice.org/articles/2024/01/09/json-schema-evolution-part-2.html) такого решения.

Schema Registry поддерживает проверку совместимости схем через конвертацию открытой схемы потребителя в закрытую. Для этого задайте в [пространстве имен](schema-registry.md#namespace) для JSON-схем [политику проверки совместимости](compatibility-check-policy.md) `Optional-friendly`. Вы можете [задать политику](../operations/create-name-space.md) для нового пространства имен или [изменить ее](../operations/update-name-space.md) для существующего пространства. Чтобы политика `Optional-friendly` работала корректно, схемы в пространстве имен должны создаваться по [закрытой контентной модели](#closed-model).

Вы также можете использовать это решение для сохранения совместимости JSON-схем при поставке данных в [Yandex Managed Service for Apache Kafka®](../../managed-kafka/index.md) через [Yandex Data Streams](../../data-streams/index.md) [Yandex Data Transfer](../../data-transfer/index.md). Подробнее о требуемых настройках эндпоинта Data Transfer — [инструкции](../../data-transfer/operations/endpoint/target/kafka.md#serializer).

## Полезные ссылки {#see-also}

* [Роберт Йокота — Понимание совместимости JSON-схем (англ.)](https://yokota.blog/2021/03/29/understanding-json-schema-compatibility/)
* [Энди Котс — Эволюция JSON-схем, часть 1 (англ.)](https://www.creekservice.org/articles/2024/01/08/json-schema-evolution-part-1.html)
* [Энди Котс — Эволюция JSON-схем, часть 2 (англ.)](https://www.creekservice.org/articles/2024/01/09/json-schema-evolution-part-2.html)