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

Entity: CurrencyEntity

Дом полей справочной записи «Валюта» контекста handbook. Проекции (lookup-выдачи, CurrencySnapshot, событие pin.handbook.currencies.v1, CurrencyId в денежных контрактах chessboard/crm/rewards/ investor-owner/promotion) ссылаются сюда и описывают только дельту.

Назначение

CurrencyEntity — реплика записи валюты из Talent Handbook (ADR-0063). Даёт всем денежным контрактам платформы типизированную ссылку CurrencyId short и display-атрибуты (код ISO-4217, название, знак) вместо ad-hoc строковых кодов. Записи создаёт и обновляет только синк источника; администратору доступны archive/restore.

Справочник валют ≠ мультивалютность (ADR-0048)

Наличие справочника валют не пересматривает ADR-0048: вся денежная арифметика PIN остаётся RUB-only. CurrencyId в контрактах — типизированная ссылка на запись справочника и источник display-атрибутов (через CurrencySnapshot), а не обещание конверсий, кросс-валютных расчётов или хранения балансов в разных валютах. Допустимость валюты в конкретной денежной операции по-прежнему ограничивается правилами ADR-0048; справочник лишь отвечает на вопрос «что это за валюта и как её показать».

IsoCode (ISO-4217) — единственное легитимное char(3) в спеке PIN: вне справочника контракты сервисов строковых кодов валют не несут — только CurrencyId + CurrencySnapshot (display-код живёт внутри снапшота, см. snapshots.md).

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

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

Поля

ПолеТипОбязательностьОграничения (PK/FK/index/constraint)ИсточникОписание
IdshortДаPKсинк (= id Talent)Идентификатор валюты = id записи источника (ADR-0063 п.2).
NamestringДаvarchar(64), min 1синкНазвание валюты («Российский рубль»).
IsoCodestringДаchar(3), upper; unique среди неархивных ux_currencies_iso_codeсинкКод ISO-4217 (RUB). В источнике поле — varchar(16) (длина не гарантирована); реплика нормализует (trim + upper) и хранит ровно char(3). INV-HND-2, INV-HND-6.
Signstring?Нетvarchar(16)синкЗнак/символ валюты («₽»).
IsArchivedboolДаdefault falseadmin / reconciliationSoft-архив (delete-gap источника, ADR-0063 п.5).
LastEventAtDateTimeOffsetДасинк (envelope CreatedAt currency-события)Время последнего применённого события источника. Заменяет ReplicaVersion: у currency-событий источника нет per-record Version, монотонность обеспечивается по времени события (envelope CreatedAt) — INV-HND-4 в time-based-форме, как у региона (region.md; ADR-0063 п.1).
CreatedAt / UpdatedAtDateTimeOffsetДасинк / adminШтампы создания/изменения.

Нормализация IsoCode: источник хранит код в varchar(16), реплика применяет trim + upper и принимает только валидный ISO-4217 — ровно 3 буквы. Значение длиной ≠ 3 после trim синк не применяет: currency-событие уходит в reject/DLQ с warn-метрикой (данные правятся в источнике) — INV-HND-6, ADR-0063 п.2. В реплику (и, как следствие, в CurrencySnapshot) попадают только валидные char(3)-коды.

Связи

СвязьТипКлючDelete behavior
CountryEntityлогическая обратная (без FK)Country.DefaultCurrencyId → Currency.Idархив валюты ссылку не ломает (резолв включает архивные, INV-HND-5)
Денежные контракты потребителей (chessboard, crm, rewards, investor-owner, promotion, messenger)логическая (чужие контексты, без FK)CurrencyId + CurrencySnapshot в событиях; resolve на admin-writeисторические ссылки переживают архив

Инварианты

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

ИнвариантУсловиеКогда проверяетсяОшибка
INV-HND-2 UniqueIsoAmongActive (применение)IsoCode уникален среди неархивных валютпри применении события синка; при admin restoreсинк: HANDBOOK_SYNC_ISO_CONFLICT → DLQ (non-retriable); restore: тот же код, HTTP 409
INV-HND-4 MonotonicVersion (time-based-форма)currency-событие применяется только при envelope CreatedAt строго больше LastEventAt; per-record Version у источника нет (ADR-0063 п.1)при применении события синка— (skip + метрика stale)
INV-HND-6 IsoCodeWellFormedIsoCode источника (varchar(16)) после trim — ровно 3 буквы (валидный ISO-4217); нормализуется в upper и хранится char(3)при применении события синкадлина ≠ 3 → HANDBOOK_SYNC_INVALID_ISO_CODE → DLQ (non-retriable) + warn-метрика

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

ОперацияАкторПредусловиеЭффектОтказ
Upsert (синк)consumer группы pin-handbook-talent-syncenvelope CreatedAt > LastEventAt (INV-HND-4); IsoCode валиден — 3 буквы после trim (INV-HND-6) и не конфликтует (INV-HND-2)создание/перезапись всех реплика-полей (snapshot-семантика) + re-publish currency-upsertedstale → skip; невалидный IsoCode → DLQ (HANDBOOK_SYNC_INVALID_ISO_CODE); ISO-конфликт → DLQ pin.handbook.talent-sync-dlq.v1 (HANDBOOK_SYNC_ISO_CONFLICT)
Auto-restore (синк)consumerзапись заархивирована, из источника пришёл её upsertIsArchived = false + warn-метрика + re-publish
Archive / Restoreадминистратор платформызапись существует; для restore — IsoCode свободен среди неархивных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: валидация CurrencyId на admin-write денежных контрактов)записи по id, включая архивные (INV-HND-5)неизвестный id отсутствует в ответе

События

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

chessboard (локальная реплика: валидация CurrencyId на write-path проектов/цен, display-атрибуты).

Ошибки

КодHTTPУсловие
HANDBOOK_ENTRY_NOT_FOUND404точечное чтение / archive / restore несуществующей валюты
HANDBOOK_SYNC_ISO_CONFLICT409 (admin restore) / DLQ, non-retriable (синк)нарушение INV-HND-2: IsoCode занят другой неархивной валютой
HANDBOOK_SYNC_INVALID_ISO_CODEDLQ, non-retriable (синк)нарушение INV-HND-6: IsoCode источника (varchar(16)) после trim не равен 3 буквам; в реплику попадают только валидные ISO-4217 (char(3))

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

АспектЗначение
ИндексыPK pk_currencies; ux_currencies_iso_code unique (upper(iso_code)) 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 логируется с актором

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

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

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