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

Entity: CountryEntity

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

Назначение

CountryEntity — реплика записи страны из внешнего справочника Talent Handbook (ADR-0063). PIN не ведёт страны самостоятельно: записи создаёт и обновляет только синк источника; администратору платформы доступны лишь archive/restore. На страну ссылаются регионы и города справочника (region.md, city.md), гео-поля проектов chessboard, документы персон profile и фильтры каталога.

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

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

Поля

ПолеТипОбязательностьОграничения (PK/FK/index/constraint)ИсточникОписание
IdshortДаPKсинк (= id Talent)Идентификатор страны = id записи источника; Guid не вводится (ADR-0063 п.2).
ShortNamestring?Нетvarchar(64)синкКраткое название («Россия»); реплицируется как есть. Основа display-имени.
FullNamestringДаvarchar(64), min 1синкПолное название («Российская Федерация»); реплицируется как есть. Fallback display-имени при пустом ShortName.
IsoCode2stringДаchar(2), upper; unique среди неархивных ux_countries_iso2синкКод ISO 3166-1 alpha-2 (RU). INV-HND-2.
IsoCode3stringДаchar(3), upper; unique среди неархивных ux_countries_iso3синкКод ISO 3166-1 alpha-3 (RUS). INV-HND-2.
DefaultCurrencyIdshort?Нетлогическая ссылка на handbook.currencies (без FK — порядок прихода событий разных справочников не гарантирован)синкВалюта страны по умолчанию (currency.md).
DefaultLanguageIdshort?Нетлогическая ссылка на handbook.languages (без FK)синкЯзык страны по умолчанию (language.md).
IsArchivedboolДаdefault falseadmin / reconciliationSoft-архив: единственный механизм вывода записи из оборота (delete-gap источника, ADR-0063 п.5).
LastEventAtDateTimeOffsetДасинк (envelope CreatedAt применённого события источника)Время последнего применённого события источника. У событий источника нет per-record версии — монотонность обеспечивается по времени публикации события (INV-HND-4, ADR-0063 п.1).
CreatedAt / UpdatedAtDateTimeOffsetДасинк / adminШтампы создания/изменения.
Display-имя

В lookup-выдачах и CountrySnapshot поле Name = ShortName ?? FullName (краткое имя, а при его отсутствии — полное) — отдельной колонки нет, вычисляется из реплицируемых ShortName/FullName (ADR-0063 п.2; конвенция — см. snapshots.md).

Стабильные seed-идентификаторы

Seed-факт источника: RU = 1, KZ = 2, BY = 3 — идентификаторы стабильны, на них допустимо полагаться в конфигурации потребителей (например, дефолт страны проекта chessboard — Россия, CountryId = 1).

Связи

СвязьТипКлючDelete behavior
RegionEntityreference (дочерние записи)Region.CountryId → Country.Idфизических удалений нет; архив страны не каскадирует на регионы/города
CityEntityreference (дочерние записи)City.CountryId → Country.Idаналогично
CurrencyEntity / LanguageEntityлогическая (без FK)DefaultCurrencyId / DefaultLanguageIdнерезолвимая ссылка допустима до прихода события соответствующего справочника
Порядок событий источника

События регионов/городов, пришедшие раньше события своей страны, не теряются: синк ретраит их до появления страны (HANDBOOK_SYNC_COUNTRY_NOT_FOUND, retriable — см. ../consumers/talent-handbook-sync.md).

Инварианты

Инварианты INV-HND-1, INV-HND-2, INV-HND-4, INV-HND-5 объявлены здесь и распространяются на все записи справочника (страны, регионы, города, валюты, языки); остальные доки контекста ссылаются на них, не переобъявляя.

ИнвариантУсловиеКогда проверяетсяОшибка
INV-HND-1 SyncOwnsDataСоздание записи и изменение реплика-полей — только синк источника; администратору доступны только archive/restore. Других write-путей в контракте нетлюбая мутация— (обеспечивается составом операций)
INV-HND-2 UniqueIsoAmongActiveISO-коды (IsoCode2, IsoCode3; для валют — IsoCode) уникальны среди неархивных записей своего справочникапри применении события синка; при admin restoreсинк: HANDBOOK_SYNC_ISO_CONFLICT → DLQ (non-retriable); restore: тот же код, HTTP 409
INV-HND-4 MonotonicByEventTimeУ событий источника нет per-record версии — монотонность обеспечивается по времени публикации: событие применяется только при envelope.CreatedAt > LastEventAt реплики; иначе — skip + метрика stale. Порядок апдейтов одной записи гарантирован партиционированием Kafka по Id (ADR-0063 п.1)при применении события синка— (skip, не ошибка)
INV-HND-5 LookupVisibilityПубличный lookup выдаёт только неархивные записи; internal resolve резолвит включая архивные — исторические ссылки (CountryId в старых документах/событиях) не ломаютсяпри чтении (lookup / resolve)точечное чтение несуществующего id → 404 HANDBOOK_ENTRY_NOT_FOUND; в batch-resolve неизвестный id просто отсутствует в ответе

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

Статусной state-машины нет: активна ↔ архив через IsArchived.

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

Постусловие любой мутации: UpdatedAt обновлён; каждое изменение состояния re-publish-ится событием (реплика + чекпоинт синка + публикация — атомарно, правила talent-replica.md).

События

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

Текущий потребитель — chessboard (локальная реплика для hot-path карточек/фильтров).

Ошибки

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

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

АспектЗначение
ИндексыPK pk_countries; ux_countries_iso2 unique (upper(iso_code_2)) where not is_archived; ux_countries_iso3 unique (upper(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 логируется с актором

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

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

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