Перейти к основному содержимому

Entity: LanguageEntity

Дом полей справочной записи «Язык» контекста handbook. Проекции (lookup-выдачи, событие pin.handbook.languages.v1) ссылаются сюда и описывают только дельту.

Назначение

LanguageEntity — реплика записи языка из Talent Handbook (ADR-0063). Реплицируется для полноты справочного контура (страны ссылаются на язык по умолчанию через DefaultLanguageId) и будущих потребителей. Записи создаёт и обновляет только синк источника; администратору доступны archive/restore.

Текущих потребителей в PIN нет

UI-локали пользователей — строки BCP-47 (ru-RU), дом закрытого списка — контекст profile; LanguageId для локали интерфейса не вводится (ADR-0063 п.3): язык справочника (атрибут контента) и локаль интерфейса (настройка пользователя) — разные концепты.

Классификация

ПолеЗначение
Контекстhandbook
KindReplica (source of truth — Talent Handbook)
Source of truthtalent.microservices.handbook, сущность Language; признак архива IsArchivedhandbook
Таблицаhandbook.languages
Tenant isolationне tenanted — платформенный справочник

Поля

ПолеТипОбязательностьОграничения (PK/FK/index/constraint)ИсточникОписание
IdshortДаPKсинк (= id Talent)Идентификатор языка = id записи источника (ADR-0063 п.2).
NamestringДаvarchar(64), min 1синкНазвание языка («Русский»).
IsoCode2stringДаchar(2), lower; unique среди неархивных ux_languages_iso2синкКод ISO 639-1 (ru). INV-HND-2.
IsoCode3stringДаchar(3), lower; unique среди неархивных ux_languages_iso3синкКод ISO 639-2/3 (rus). INV-HND-2.
FlagIsoCode2string?Нетchar(2), upperсинкISO 3166-1 alpha-2 код страны для отрисовки флага языка в UI (display-атрибут; логическая ссылка на страну по ISO-коду, без FK).
IsArchivedboolДаdefault falseadmin / reconciliationSoft-архив (delete-gap источника, ADR-0063 п.5).
LastEventAtDateTimeOffsetДасинк (envelope CreatedAt language-события)Время последнего применённого события источника. Заменяет ReplicaVersion: у language-событий источника нет per-record Version, монотонность обеспечивается по времени события (envelope CreatedAt) — INV-HND-4 в time-based-форме, как у региона (region.md; ADR-0063 п.1).
CreatedAt / UpdatedAtDateTimeOffsetДасинк / adminШтампы создания/изменения.

Связи

СвязьТипКлючDelete behavior
CountryEntityлогическая обратная (без FK)Country.DefaultLanguageId → Language.Idархив языка ссылку не ломает (резолв включает архивные, INV-HND-5)

Инварианты

INV-HND-1 (создаёт/меняет только синк), INV-HND-2 (уникальность ISO среди неархивных), INV-HND-4 (монотонность применения — в time-based-форме по LastEventAt, см. ниже), INV-HND-5 (lookup — только неархивные; resolve — включая архивные) — объявлены в country.md и действуют для языка. Применение к языку (коды IsoCode2/IsoCode3):

ИнвариантУсловиеКогда проверяетсяОшибка
INV-HND-2 UniqueIsoAmongActive (применение)IsoCode2 и IsoCode3 уникальны среди неархивных языковпри применении события синка; при admin restoreсинк: HANDBOOK_SYNC_ISO_CONFLICT → DLQ (non-retriable); restore: тот же код, HTTP 409
INV-HND-4 MonotonicVersion (time-based-форма)language-событие применяется только при envelope CreatedAt строго больше LastEventAt; per-record Version у источника нет (ADR-0063 п.1)при применении события синка— (skip + метрика stale)

Жизненный цикл и операции

ОперацияАкторПредусловиеЭффектОтказ
Upsert (синк)consumer группы pin-handbook-talent-syncenvelope CreatedAt > LastEventAt (INV-HND-4); ISO-коды не конфликтуют (INV-HND-2)создание/перезапись всех реплика-полей (snapshot-семантика) + re-publish language-upsertedstale → skip; ISO-конфликт → DLQ pin.handbook.talent-sync-dlq.v1 (HANDBOOK_SYNC_ISO_CONFLICT)
Auto-restore (синк)consumerзапись заархивирована, из источника пришёл её upsertIsArchived = false + warn-метрика + re-publish
Archive / Restoreадминистратор платформызапись существует; для restore — ISO-коды свободны среди неархивныхIsArchived переключён; re-publish; повторный вызов — идемпотентный no-op404 HANDBOOK_ENTRY_NOT_FOUND; restore: 409 HANDBOOK_SYNC_ISO_CONFLICT
Lookup (list)любой (включая гостя)неархивные языки (INV-HND-5)
Resolve (batch by id)внутренние сервисы (m2m)записи по id, включая архивные (INV-HND-5)неизвестный id отсутствует в ответе

События

СобытиеТопикКогда публикуетсяPayload
language-upsertedpin.handbook.languages.v1 (compacted, key = Id, ContractVersion 1)применённый upsert синка; admin archive/restore; пересинхронизацияполный snapshot реплики, включая IsArchived и LastEventAt../events/language-upserted.md
Текущих подписчиков нет

Локальная реплика chessboard подписана на четыре топика справочника — без языков; топик публикуется для будущих потребителей: compacted-хранение позволяет подключиться в любой момент чтением с offset 0.

Ошибки

КодHTTPУсловие
HANDBOOK_ENTRY_NOT_FOUND404точечное чтение / archive / restore несуществующего языка
HANDBOOK_SYNC_ISO_CONFLICT409 (admin restore) / DLQ, non-retriable (синк)нарушение INV-HND-2: ISO-код занят другим неархивным языком

Индексы, хранение, безопасность

АспектЗначение
ИндексыPK pk_languages; ux_languages_iso2 unique (lower(iso_code_2)) where not is_archived; ux_languages_iso3 unique (lower(iso_code_3)) where not is_archived
Партиционирование / retentionnone — десятки строк; retention бессрочный
Concurrencyсобытия применяются последовательно per-key; admin-операции — оптимистическая блокировка
Permissions / PIIarchive/restore — admin API (handbook.entries.manage); lookup — публичный; resolve — internal m2m. PII нет
AuditLastEventAt + штампы; admin archive/restore логируется с актором

Обратные ссылки

Автоссылки: где используется этот документ.

Связанные документы