Event Contract: language-upserted (LanguageUpsertedEvent)
Версионируемый контракт интеграционного события «запись языка справочника создана/изменена».
Consumer-документы ссылаются сюда, не переописывая payload. Дом полей —
entity: LanguageEntity.
Назначение
Фиксирует каждое изменение состояния реплики языка в контексте handbook. Текущих потребителей в PIN
нет — топик публикуется как задел: справочник языков реплицируется для полноты контура (страны ссылаются
на язык по умолчанию через DefaultLanguageId), а compacted-хранение позволяет будущему потребителю
подключиться в любой момент чтением топика с offset 0 — без доработок producer. UI-локали пользователей —
строки BCP-47, дом закрытого списка — контекст profile; LanguageId для локали интерфейса не вводится
(ADR-0063 п.3).
Metadata
| Поле | Значение |
|---|---|
| Event type | language-upserted |
| Topic | pin.handbook.languages.v1 — compacted (cleanup.policy = compact); последняя запись каждого языка хранится бессрочно |
| Partitions / key | 1 (десятки строк, строгий порядок) / key = Id записи |
| ContractVersion | 1 |
| Producer | handbook: применённый синк (talent-handbook-sync), admin archive/restore (archive-restore-entry), полная пересинхронизация (talent-initial-import) |
| Статус | draft |
Событие публикуется атомарно с изменением реплики (реплика + чекпоинт синка + публикация — одно согласованное изменение, правила talent-replica.md); после фиксации — доставляется гарантированно (at-least-once).
Payload LanguageUpsertedEvent
Полный snapshot реплики языка на момент публикации. Служебные штампы CreatedAt/UpdatedAt в payload
не входят.
| Поле | Тип | Обязательность | Источник (поле реплики) | Compatibility | Описание |
|---|---|---|---|---|---|
Id | short | Да | LanguageEntity.Id | Immutable | Идентификатор языка = id источника (= Kafka key). |
Name | string | Да | LanguageEntity.Name | Additive only | Название языка («Русский»), ≤ 64. |
IsoCode2 | string | Да | LanguageEntity.IsoCode2 | Additive only | Код ISO 639-1 (ru), char(2), lower. |
IsoCode3 | string | Да | LanguageEntity.IsoCode3 | Additive only | Код ISO 639-2/3 (rus), char(3), lower. |
FlagIsoCode2 | string? | Нет | LanguageEntity.FlagIsoCode2 | Additive only | ISO 3166-1 alpha-2 код страны для отрисовки флага языка в UI (display-атрибут), char(2), upper. |
IsArchived | bool | Да | LanguageEntity.IsArchived | Additive only | Soft-архив: true — запись выведена из оборота. Tombstone-записей в топике нет — архив передаётся значением. |
ReplicaVersion | long | Да | LanguageEntity.ReplicaVersion | Immutable semantics | Версия записи источника; монотонный guard у потребителей (INV-HND-4). |
Когда публикуется
| Условие | Изменение реплики | Публикация |
|---|---|---|
Применённый upsert синка Talent (handbooks.after-language-information-update), включая auto-restore заархивированной записи | создание / перезапись реплика-полей / IsArchived = false | атомарно с изменением реплики |
| Admin archive / restore | IsArchived переключён | та же |
Полная пересинхронизация (Talent POST sync-handbooks re-эмитит все строки; см. talent-initial-import) | re-emit всех записей справочника | та же |
Потребители (consumers)
Локальная реплика chessboard подписана на четыре топика справочника, языки в её состав не входят
(handbook-sync). Будущий потребитель обязан соблюдать правила
раздела «Порядок, идемпотентность и bootstrap» и заводится additive: подписка + строка в этой таблице,
доработок producer не требуется.
Порядок, идемпотентность и bootstrap
- Порядок per-key. Key =
Id— все события одного языка строго упорядочены; потребитель держит guard поReplicaVersion(событие с версией ≤ применённой — skip + метрика stale). - Полный snapshot — замещение, не слияние. Потребитель перезаписывает свою реплику целиком; отсутствие
optional-поля
FlagIsoCode2означает «значение очищено», а не «оставить прежнее». - Compaction. Промежуточные версии записи могут быть вычищены компакцией — каждое событие
самодостаточно. Физических удалений и tombstone-ов нет: вывод из оборота — значением
IsArchived = true. - Bootstrap. Новый потребитель получает полный актуальный справочник чтением топика с offset 0.
Доставка и повторная обработка (retry/DLQ)
| Аспект | Правило |
|---|---|
| Гарантия доставки | at-least-once; публикация атомарна с изменением реплики |
| Повтор у потребителя | повторная доставка того же события — идемпотентный no-op (version-guard) |
| Transient-ошибка обработки | retry с экспоненциальным backoff, offset не коммитится |
| Неустранимая ошибка обработки | событие — в собственный DLQ потребителя + алерт (требование к будущим потребителям) |
| Отказ применения на стороне producer | событие не публикуется: неприменимое событие источника (HANDBOOK_SYNC_ISO_CONFLICT, non-retriable) ушло в pin.handbook.talent-sync-dlq.v1 (см. talent-handbook-sync), реплика и топик не изменились |
Версионирование и совместимость
- Версия — один
ContractVersion = 1; consumer при несоответствии версии события логирует warning и продолжает обработку. Топик кодирует версию суффиксом.v1. - Additive-only: новые поля добавляются как optional с безопасным default — без bump; неизвестные поля потребитель игнорирует.
- Breaking — новый
ContractVersion = 2+ новый топикpin.handbook.languages.v2+ dual-publish + deadline миграции consumers + sunset.v1(пока потребителей нет — breaking сводится к пересозданию топика, но правило фиксируется заранее). - Новый потребитель подключается в любой момент — bootstrap чтением compacted-топика с offset 0.
Безопасность
| Поле | Класс данных | Правило |
|---|---|---|
| все поля payload | Public (нормативный справочник) | include |
PII, secrets, tokens в событии отсутствуют — данные публичные справочные.
Обратные ссылки
Автоссылки: где используется этот документ.
- Домен (CQRS) (2): Реплика Talent Handbook: правила синка + Entity HandbookSyncState, Entity: LanguageEntity
- Use Cases (1): UC-HND-002. Система синхронизирует реплику справочника из Talent
- События (4): Event Contract: city-upserted (CityUpsertedEvent), Event Contract: country-upserted (CountryUpsertedEvent), Event Contract: currency-upserted (CurrencyUpsertedEvent), Event Contract: region-upserted (RegionUpsertedEvent)
- Консюмеры (1): Consumer: Talent Handbook Sync (pin-handbook-talent-sync)
- index.md (1): Handbook Service
- registries (1): Реестр событий PIN
Связанные документы
- Сущность: language.md (дом полей, INV-HND-2/4; «текущих потребителей нет»);
DefaultLanguageIdстраны: country.md. - Producer: talent-handbook-sync, archive-restore-entry, talent-initial-import.
- Соседние контракты справочника: country-upserted.md, region-upserted.md, city-upserted.md, currency-upserted.md.
- ADR-0063 п.3 (locale/timezone — не справочники handbook).