Вы, вероятно, позволяли базам данных генерировать ID для сущностей по крайней мере один раз.
Но что, если я скажу вам, что при разработке приложений есть альтернатива?
Я уверен, что это утверждение резко контрастирует с тем, что вы узнали в бесчисленных уроках на Youtube, которые смотрели, когда учились команде CREATE TABLE UsingTerribleIds ()
.
Создавайте свои ID на уровне приложения. Не на уровне хранения.
Вот и все.
(Обратите внимание, что ID сущности необязательно является первичным ключом БД).
Расскажу о том, почему сгенерированные базой данных ID ужасны, и как я подхожу к генерации ID в клиентских проектах.
Итак, в чем же проблема с тем, чтобы база данных генерировала ID приложения?
- Самая большая проблема заключается в том, что вы делегируете важный аспект приложения стороннему ПО. Вы теряете контроль над своим приложением в тот момент, когда снимаете с себя ответственность.
- Вы, вероятно, будете применять плохие практики при проектировании классов сущностей только для того, чтобы облегчить работу с фреймворком для хранения, таким как EntityFramework в C# .NET.
Одна из самых страшных ошибок, которую я вижу у junior-программистов — это публичные ID.
Публичный установщик говорит миру, что изменять ID этой сущности нормально — это может быть ужасной идеей.
3. Вы делаете модульное тестирование более сложным, чем оно должно быть. Вдруг полагаетесь на третью сторону, предоставляющую вашим сущностям ID.
Допустим, вы поняли, что публичный установщик ID — это, по сути, ужасный выбор, и также не хотите, чтобы вызывающий код устанавливал ID. Вместо этого ваш класс будет выглядеть примерно так, как показано ниже.
Ваш ORM, возможно, все еще способен установить поле ID через рефлексию — от нее не скрыться…
Но как бы вы провели модульное тестирование? Поле id устанавливается равным 0 при создании экземпляра. Создание нескольких экземпляров TerribleBook приведет к коллизии сущностей, так как теперь несколько TerribleBook имеют один и тот же ID, даже если должен представлять две отдельные сущности.
Пример ID, сгенерированного доменом
Решение очень простое. Просто взгляните на фрагмент ниже.
Давайте пройдемся по этому коду, так как каждое изменение от TerribleBook к FixedBook может быть неочевидным.
Мы делаем конструктор частным и создаем экземпляры новых объектов с помощью статического фабричного метода. Это позволяет нам абстрагировать логику создания экземпляра от вызывающего объекта и даже дает возможность использовать полиморфизм — мы можем вернуть объект Null вместо того, чтобы выбрасывать его.
Обратите внимание, что мы все еще принимаем ID в качестве параметра конструктора. Однако мы отвечаем за генерацию и предоставление ID (на строке 18). Не база данных.
Тип данных ID сущности изменяется с int на string для того, чтобы дать возможность использовать что-то значимое. Однако остерегайтесь проблем производительности и последствий использования строк в качестве ID.
Идентификатор Guid.NewGuid().ToString("D")
гарантирует, что мы получим GUID в пунктирном формате. Мне нравится использовать GUID, но вы можете создавать свои собственные ID, которые будут наилучшим образом подходить для потребностей вашего бизнеса и приложения.
Обратите внимание, что я не говорю о том, что именно нужно использовать в качестве первичного ключа. Кроме того, я не утверждаю, что это решение без компромиссов. Лучший дизайн сущности поставляется с потенциально худшей производительностью.
Вам придется принимать собственные решения о том, что лучше в конкретном сценарии.
Читайте также:
Перевод статьи Nicklas Millard: Don’t Use Database Generated IDs