Entity: LanguageEntity
Дом полей справочной записи «Язык» контекста handbook. Проекции (lookup-выдачи, событие
pin.handbook.languages.v1) ссылаются сюда и описывают только дельту.
Назначение
LanguageEntity — реплика записи языка из Talent Handbook
(ADR-0063). Реплицируется для
полноты справочного контура (страны ссылаются на язык по умолчанию через DefaultLanguageId) и будущих
потребителей. Записи создаёт и обновляет только синк источника; администратору доступны archive/restore.
UI-локали пользователей — строки BCP-47 (ru-RU), дом закрытого списка — контекст profile;
LanguageId для локали интерфейса не вводится (ADR-0063 п.3): язык справочника (атрибут контента)
и локаль интерфейса (настройка пользователя) — разные концепты.
Классификация
| Поле | Значение |
|---|---|
| Контекст | handbook |
| Kind | Replica (source of truth — Talent Handbook) |
| Source of truth | talent.microservices.handbook, сущность Language; признак архива IsArchived — handbook |
| Таблица | handbook.languages |
| Tenant isolation | не tenanted — платформенный справочник |
Поля
| Поле | Тип | Обязательность | Ограничения (PK/FK/index/constraint) | Источник | Описание |
|---|---|---|---|---|---|
Id | short | Да | PK | синк (= id Talent) | Идентификатор языка = id записи источника (ADR-0063 п.2). |
Name | string | Да | varchar(64), min 1 | синк | Название языка («Русский»). |
IsoCode2 | string | Да | char(2), lower; unique среди неархивных ux_languages_iso2 | синк | Код ISO 639-1 (ru). INV-HND-2. |
IsoCode3 | string | Да | char(3), lower; unique среди неархивных ux_languages_iso3 | синк | Код ISO 639-2/3 (rus). INV-HND-2. |
FlagIsoCode2 | string? | Нет | char(2), upper | синк | ISO 3166-1 alpha-2 код страны для отрисовки флага языка в UI (display-атрибут; логическая ссылка на страну по ISO-коду, без FK). |
IsArchived | bool | Да | default false | admin / reconciliation | Soft-архив (delete-gap источника, ADR-0063 п.5). |
LastEventAt | DateTimeOffset | Да | — | синк (envelope CreatedAt language-события) | Время последнего применённого события источника. Заменяет ReplicaVersion: у language-событий источника нет per-record Version, монотонность обеспечивается по времени события (envelope CreatedAt) — INV-HND-4 в time-based-форме, как у региона (region.md; ADR-0063 п.1). |
CreatedAt / UpdatedAt | DateTimeOffset | Да | — | синк / 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-sync | envelope CreatedAt > LastEventAt (INV-HND-4); ISO-коды не конфликтуют (INV-HND-2) | создание/перезапись всех реплика-полей (snapshot-семантика) + re-publish language-upserted | stale → skip; ISO-конфликт → DLQ pin.handbook.talent-sync-dlq.v1 (HANDBOOK_SYNC_ISO_CONFLICT) |
| Auto-restore (синк) | consumer | запись заархивирована, из источника пришёл её upsert | IsArchived = false + warn-метрика + re-publish | — |
| Archive / Restore | администратор платформы | запись существует; для restore — ISO-коды свободны среди неархивных | IsArchived переключён; re-publish; повторный вызов — идемпотентный no-op | 404 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-upserted | pin.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_FOUND | 404 | точечное чтение / archive / restore несуществующего языка |
HANDBOOK_SYNC_ISO_CONFLICT | 409 (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 |
| Партиционирование / retention | none — десятки строк; retention бессрочный |
| Concurrency | события применяются последовательно per-key; admin-операции — оптимистическая блокировка |
| Permissions / PII | archive/restore — admin API (handbook.entries.manage); lookup — публичный; resolve — internal m2m. PII нет |
| Audit | LastEventAt + штампы; admin archive/restore логируется с актором |
Обратные ссылки
Автоссылки: где используется этот документ.
- Домен (CQRS) (2): Реплика Talent Handbook: правила синка + Entity HandbookSyncState, Entity: CountryEntity
- API (4): GET /api/v1/handbook/{countries,regions,cities,currencies,languages} — Публичный lookup справочников, Handbook API — реестр операций областей public / internal / admin, POST /api/v1/admin/handbook/{type}/{id}/{archive,restore} — Архив и восстановление справочной записи, POST /internal/handbook/resolve — Batch-резолв справочных записей по id
- Use Cases (2): UC-HND-002. Система синхронизирует реплику справочника из Talent, US-реестр контекста handbook (US-HND)
- События (1): Event Contract: language-upserted (LanguageUpsertedEvent)
- Консюмеры (1): Consumer: Talent Handbook Sync (pin-handbook-talent-sync)
- Фоновые задачи (1): Background Job: talent-initial-import (bootstrap и reconciliation справочников из Talent Handbook)
- index.md (1): Handbook Service
- registries (1): Реестр сущностей PIN
Связанные документы
- country.md — INV-HND-1/4/5 и
DefaultLanguageId. - talent-replica.md — правила синка; ../consumers/talent-handbook-sync.md — consumer; ../events/language-upserted.md — контракт события.
- ADR-0063 п.3 — locale (BCP-47)
и timezone (IANA) — не справочники handbook; дом списка локалей —
profile/domain/profile.md.