Зачем в модели данных разделять технический идентификатор сущности и её бизнес-ключ?
Технический идентификатор нужен как стабильная внутренняя ссылка на запись, а бизнес-ключ — для идентификации сущности по правилам предметной области. Их разделение позволяет изменять бизнес-атрибуты без каскадного обновления связей и не смешивать технические детали хранения с бизнес-смыслом.
Бизнес-ключ при этом обычно должен иметь ограничение уникальности, а технический идентификатор — оставаться неизменным на протяжении жизненного цикла записи.
В ранних и небольших системах естественный атрибут часто использовали одновременно как идентификатор записи и как бизнес-признак: например, номер договора или адрес электронной почты. Это казалось простым, пока такие значения не потребовалось изменить, переиспользовать или связать с несколькими процессами.
Разделение стало практикой при усложнении моделей данных и появлении большого числа внешних связей. Оно решает проблему нестабильных бизнес-правил, не превращая изменение атрибута предметной области в миграцию всех зависимых ссылок.
Предположим, номер договора используется как первичный ключ и на него ссылаются платежи, документы и интеграционные журналы. Если правила нумерации изменились или договору исправили номер, нужно обновить все ссылки, индексы, кэши и, возможно, данные во внешних системах.
Если же бизнес-ключ не ограничить уникальностью, появятся две записи с одинаковым номером договора. Тогда поиск по бизнес-правилу станет неоднозначным, а технический идентификатор сам по себе не защитит систему от логической ошибки.
В модели вводят отдельный неизменяемый технический идентификатор, например внутренний идентификатор записи. Все связи между таблицами и сущностями строят по нему. Бизнес-ключ хранится отдельным атрибутом и защищается уникальным ограничением в пределах нужной области действия.
Область уникальности нужно определить явно. Номер может быть уникален глобально, внутри организации, среди активных записей или в сочетании нескольких полей. Это не только свойство базы данных, но и часть требований к предметной области.
Изменение бизнес-ключа тогда не меняет идентичность самой сущности: обновляется один атрибут, а внутренние связи остаются прежними. При этом внешние API не обязаны раскрывать технический идентификатор: наружу можно отдавать бизнес-ключ или отдельный публичный идентификатор, если внутренний ключ раскрывать нежелательно.
У подхода есть компромиссы. Появляются дополнительные поля, индексы и необходимость поддерживать уникальность, а неправильный выбор области уникальности может блокировать допустимые операции. Кроме того, технический идентификатор не отменяет требований к формату, проверке и жизненному циклу бизнес-ключа.
В системе логистики код маршрута сначала считался уникальным и использовался как ключ во всех связанных сущностях. Позже выяснилось, что после закрытия маршрута тот же код может быть выдан повторно в новом операционном периоде.
Рассматривались два варианта. Первый — сохранять код маршрута первичным ключом и добавлять период в каждый внешний ключ: это сохраняло видимую структуру, но усложняло все связи и миграции. Второй — ввести стабильный внутренний идентификатор, а уникальность кода задать как составную: код плюс операционный период.
Выбрали второй вариант. Связи стали ссылаться на внутренний идентификатор, а бизнес-правило повторного использования кода выражалось отдельно и явно. В результате изменение кода и повторное использование его в другом периоде не приводили к перепривязке исторических грузов и документов.
Нет, если параллельные операции могут одновременно пройти проверку отсутствия значения. Ограничение уникальности должно быть закреплено на уровне хранилища либо обеспечено эквивалентным механизмом сериализации. Проверка в приложении полезна для понятного сообщения об ошибке, но сама по себе не защищает от гонки.
Нет. Если бизнес-ключ действительно стабилен, однозначен, компактен и никогда не меняется, естественный ключ может быть приемлемым. Однако это решение требует явно доказать стабильность всех его составляющих; предположение, что значение «обычно не меняется», недостаточно для надежной модели.
Скрытие в API не устраняет проблему модели. Внутренние связи всё равно должны опираться на стабильную идентичность, а бизнес-ключ — иметь ясные правила уникальности, изменения и повторного использования. Кроме того, внешним потребителям может потребоваться стабильный публичный идентификатор, который не совпадает ни с внутренним ключом, ни с изменяемым бизнес-атрибутом.