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

Entity: RegionEntity

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

Назначение

RegionEntity — регион (субъект/область) страны. В PIN это первоклассная запись справочника со своим топиком и идентификатором, хотя у источника (Talent Handbook) собственного агрегата и события региона нет: строка региона приходит только вложенной моделью RegionEventModel внутри city-событий и derive-ится синком (ADR-0063 п.4). На регион ссылаются города справочника (city.md) и фильтр-ключ RegionId каталога/карточек проектов.

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

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

Поля

ПолеТипОбязательностьОграничения (PK/FK/index/constraint)ИсточникОписание
IdlongДаPKсинк (= id CountryRegion Talent)Идентификатор региона = id записи источника (ADR-0063 п.2).
CountryIdshortДаFK → handbook.countries; index ix_regions_country_idсинк (= CountryId вмещающего city-события)Страна региона. RegionEventModel источника CountryId не несёт — страна берётся из city-события, внутри которого регион приехал (ADR-0063 п.4). INV-HND-3: регион принадлежит ровно одной стране.
NamestringДаvarchar(256), min 1синк (RegionEventModel.Name)Название региона («Самарская область»).
IsArchivedboolДаdefault falseadmin / reconciliationSoft-архив (delete-gap источника, ADR-0063 п.5).
LastEventAtDateTimeOffsetДасинк (envelope CreatedAt city-события)Время последнего применённого события источника. У источника нет собственного версионирования региона (и per-record версии у событий вообще нет) — монотонность обеспечивается по времени события (INV-HND-4, ADR-0063 п.1).
CreatedAt / UpdatedAtDateTimeOffsetДасинк / adminШтампы создания/изменения.

Особенности наполнения (derive из city-событий)

  1. Отдельного region-события у источника нет. При применении city-события (handbooks.after-city-information-update) синк берёт вложенную RegionEventModel = { RegionId (long), Name } (без CountryId) и создаёт/обновляет запись региона; CountryId региона берётся из CountryId вмещающего city-события (город, регион и страна в одном событии консистентны, ADR-0063 п.4). Регион без единого пришедшего города в PIN не материализуется (нет источника его CountryId). Изменение региона re-publish-ится собственным событием region-upserted.
  2. Монотонность — по времени события: применяется только при envelope.CreatedAt city-события > LastEventAt; более старое событие — skip + метрика stale (INV-HND-4 в time-based-форме). Порядок апдейтов гарантирован партиционированием Kafka по Id записи-источника.
  3. Порядок применения geo-справочников: страна → регион → город. Если страна региона ещё не реплицирована — retriable-ошибка HANDBOOK_SYNC_COUNTRY_NOT_FOUND (retry с backoff, событие не теряется).
Известное ограничение распространения переименований

Переименование региона в источнике доезжает до PIN только вместе с city-событием любого из его городов. Если ни одно city-событие не пришло — имя региона в реплике устаревает. Ограничение принято как допустимое для редко меняющихся geo-данных и гарантированно закрывается полной пересинхронизацией (../background/talent-initial-import.md); staleness отслеживается метрикой (см. ../consumers/talent-handbook-sync.md).

Связи

СвязьТипКлючDelete behavior
CountryEntityreference (родитель)CountryId → Country.Idфизических удалений нет; архив страны не каскадирует
CityEntityreference (дочерние записи)City.RegionId → Region.Idархив региона не каскадирует на города

Инварианты

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

ИнвариантУсловиеКогда проверяетсяОшибка
INV-HND-3 GeoConsistency (аспект региона)Регион принадлежит ровно одной стране: CountryId вмещающего city-события (страна региона RegionEventModel не несёт) совпадает с CountryId существующей записи региона (перенос региона между странами источником не поддерживается)при применении city-событияHANDBOOK_SYNC_GEO_MISMATCH → DLQ pin.handbook.talent-sync-dlq.v1 (non-retriable, разрешение — правка данных в источнике)
INV-HND-4 MonotonicByEventTime (time-based-форма)Регион применяется только при envelope.CreatedAt city-события строго больше LastEventAtпри применении city-события— (skip + метрика stale)

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

ОперацияАкторПредусловиеЭффектОтказ
Upsert (derive из city-события)consumer группы pin-handbook-talent-syncстрана (CountryId city-события) существует; envelope.CreatedAt > LastEventAt; CountryId не меняется (INV-HND-3)создание/обновление Name/LastEventAt + re-publish region-upserted (только если запись фактически изменилась)страны нет → retry HANDBOOK_SYNC_COUNTRY_NOT_FOUND; смена страны → DLQ HANDBOOK_SYNC_GEO_MISMATCH; stale → skip
Auto-restore (синк)consumerзапись заархивирована, регион снова пришёл в city-событииIsArchived = false + warn-метрика + re-publish
Archive / Restoreадминистратор платформызапись существуетIsArchived переключён; re-publish; повторный вызов — идемпотентный no-op404 HANDBOOK_ENTRY_NOT_FOUND
Lookup (list по стране)любой (включая гостя)неархивные регионы страны (INV-HND-5)
Resolve (batch by id)внутренние сервисы (m2m)записи по id, включая архивные (INV-HND-5)неизвестный id отсутствует в ответе

События

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

chessboard (локальная реплика; при переименовании региона обновляет денормализованные RegionName проектов и re-publish-ит затронутые проекты).

Ошибки

КодHTTPУсловие
HANDBOOK_ENTRY_NOT_FOUND404точечное чтение / archive / restore несуществующего региона
HANDBOOK_SYNC_COUNTRY_NOT_FOUND— (синк, retriable)city-событие с регионом пришло раньше события своей страны; retry с backoff, затем DLQ + алерт
HANDBOOK_SYNC_GEO_MISMATCH— (синк, non-retriable → DLQ)нарушение INV-HND-3: CountryId вмещающего city-события не совпадает со страной существующего региона

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

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

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

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

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