После пары полезных ответов о том, должен ли я использовать объект домена или уникальный идентификатор в качестве параметра метода / функции здесь Идентификатор против объекта домена в качестве параметра метода , у меня возникает похожий вопрос re: members (предыдущее обсуждение вопросов не удалось покрыть это). Каковы плюсы и минусы использования уникальных идентификаторов в качестве члена против объекта в качестве члена. Я спрашиваю в отношении строго типизированных языков, таких как Scala / C # / Java. Должен ли я иметь (1)
User( id: Int, CurrentlyReadingBooksId: List[Int])
Book( id: Int, LoanedToId: Int )
или (2), предпочтительнее (1) После прохождения: должны ли мы определить типы для всего?
User( id: UserId, CurrentlyReadingBooksId: List[ BookId] )
Book( id: BookId, LoanedToId: UserId )
или (3)
User( id: Int, CurrentlyReadingBooks: List[Book])
Book( id: Int, LoanedTo: User)
Хотя я не могу думать о преимуществах наличия объекта (3), одно из преимуществ наличия идентификаторов (2) и (1) состоит в том, что когда я создаю объект User из БД, мне не нужно создавать объект Book, который может, в свою очередь, зависеть от самого объекта User, создавая бесконечную цепочку. Существует ли общее решение этой проблемы как для RDBMS, так и для No-SQL (если они разные)?
Основываясь на некоторых ответах, перефразирующих мой вопрос: (с использованием идентификаторов, которые должны быть в упакованных типах) 1) Всегда использовать идентификаторы? 2) Всегда использовать объекты? 3) Использовать идентификаторы, когда существует риск рекурсии в сериализации и десериализации, но использовать объекты иначе? 4) Что-нибудь еще?
РЕДАКТИРОВАТЬ: Если вы отвечаете, что объекты должны использоваться всегда или в некоторых случаях, пожалуйста, убедитесь, что ответили на самую большую проблему, которую другие ответчики опубликовали => Как получить данные из БД
источник
Ответы:
Доменные объекты как идентификаторы создают некоторые сложные / тонкие проблемы:
Сериализация / десериализации
Если вы храните объекты в качестве ключей, это очень усложнит сериализацию графа объектов. Вы получите
stackoverflow
ошибки при выполнении наивной сериализации в JSON или XML из-за рекурсии. Затем вам нужно будет написать собственный сериализатор, который преобразует фактические объекты, чтобы использовать их идентификаторы вместо сериализации экземпляра объекта и создания рекурсии.Передавайте объекты для безопасности типов, но сохраняйте только идентификаторы, тогда у вас может быть метод доступа, который лениво загружает связанный объект при его вызове. Кэширование второго уровня позаботится о последующих вызовах.
Тонкие эталонные утечки:
Если вы используете доменные объекты в таких же конструкторах, как и у вас, вы создадите циклические ссылки, которые будут очень трудными для восстановления памяти для объектов, которые не используются активно.
Идеальная ситуация:
Непрозрачные идентификаторы против int / long:
id
Должна быть полностью непрозрачным идентификатором , который не несет никакой информации о том, что она идентифицирует. Но он должен предложить некоторую проверку того, что он является действительным идентификатором в своей системе.Сырые типы ломают это:
int
,long
ИString
наиболее часто используемые типы сырья для идентификаторов в системе RDBMS. Практические причины, которые датируются десятилетиями, имеют долгую историю, и все они являются компромиссами, которые либо вписываются в сбережения,space
либо в сбережения,time
либо в оба варианта.Последовательные идентификаторы являются худшими нарушителями:
Когда вы используете последовательный идентификатор, вы по умолчанию упаковываете временную семантическую информацию в идентификатор. Что не плохо, пока не используется. Когда люди начинают писать бизнес-логику, которая сортирует или фильтрует семантическое качество идентификатора, тогда они создают боль для будущих сопровождающих.
String
поля являются проблематичными, потому что наивные дизайнеры будут упаковывать информацию в содержимое, обычно также во временную семантику.Это также делает невозможным создание распределенной системы данных, поскольку
12437379123
она не является уникальной в глобальном масштабе. Вероятность того, что другой узел в распределенной системе создаст запись с таким же номером, в значительной степени гарантируется, когда вы получаете достаточно данных в системе.Затем вокруг него начинают работать хаки, и все это превращается в кучу дымящегося беспорядка.
Игнорирование огромных распределенных систем ( кластеров ) становится полным кошмаром, когда вы начинаете пытаться обмениваться данными с другими системами. Особенно, когда другая система не находится под вашим контролем.
Вы сталкиваетесь с точно такой же проблемой, как сделать свой идентификатор глобально уникальным.
UUID был создан и стандартизирован по причине:
UUID
может страдать от всех проблем, перечисленных выше, в зависимости от того, чтоVersion
вы используете.Version 1
использует MAC-адрес и время для создания уникального идентификатора. Это плохо, потому что несет семантическую информацию о месте и времени. Это само по себе не проблема, это когда наивные разработчики начинают полагаться на эту информацию для бизнес-логики. Это также приводит к утечке информации, которая может быть использована при любых попытках вторжения.Version 2
использует пользователейUID
илиGID
и domianUID
илиGUI
вместо этого времяVersion 1
так же плохо, какVersion 1
для утечки данных и риска этой информации для использования в бизнес-логике.Version 3
аналогично, но заменяет MAC-адрес и времяMD5
хэшем некоторого массиваbyte[]
из чего-то, что определенно имеет семантическое значение. Существует нет утечки данных, чтобы беспокоиться,byte[]
не может быть восстановлено изUUID
. Это дает вам хороший способ детерминированного созданияUUID
экземпляров формы и какого-либо внешнего ключа .Version 4
основывается только на случайных числах, что является хорошим решением, оно не несет в себе никакой семантической информации, но оно не детерминировано воссоздано.Version 5
так же, как,Version 4
но используетsha1
вместоmd5
.Ключи домена и ключи данных транзакций
Я предпочитаю идентификаторы объекта домена, использовать
Version 5
или,Version 3
еслиVersion 5
по каким-либо техническим причинам, запрещено использовать .Version 3
отлично подходит для данных транзакций, которые могут быть распределены по многим машинам.Если вы не ограничены пространством, используйте UUID:
Они гарантированно уникальны, выгружая данные из одной базы данных и загружая их в другую, вам никогда не приходилось беспокоиться о дублирующих идентификаторах, которые фактически ссылаются на разные данные домена.
Version 3,4,5
полностью непрозрачны, и именно так они и должны быть.Вы можете иметь один столбец в качестве первичного ключа с,
UUID
а затем вы можете иметь составные уникальные индексы для того, что было бы естественным составным первичным ключом.Хранения не должно быть
CHAR(36)
либо. Вы можете хранитьUUID
в собственном поле байтов / битов / чисел для данной базы данных, пока она еще индексируется.наследие
Если у вас есть необработанные типы, и вы не можете их изменить, вы все равно можете абстрагировать их в своем коде.
Использование
Version 3/5
of of ofUUID
вы можете передатьClass.getName()
+String.valueOf(int)
как abyte[]
и иметь непрозрачный ссылочный ключ, который может быть восстановлен и детерминирован.источник
C-> A -> B -> A
иB
помещено вCollection
тогда,A
и все его дети все еще достижимы, эти вещи не совсем очевидны и могут привести к незначительным утечкам .GC
Это наименьшая из проблем, сериализация и десериализация графа - это сложный кошмар.Да, в любом случае есть и преимущества, и компромисс.
List<int>
:User
Users
таблицеList<Book>
:Если у вас нет проблем с памятью или процессором, я бы пошел с
List<Book>
, с которыми , код, использующийUser
экземпляры, будет чище.Компромисс:
При использовании Linq2SQL код, сгенерированный для сущности User, будет иметь
EntitySet<Book>
ленивую загрузку при доступе к нему. Это должно сохранить ваш код в чистоте, а экземпляр User - маленьким (след памяти).источник
Краткое и простое правило:
Идентификаторы используются в DTO .
Ссылки на объекты обычно используются в объектах уровня логики домена / бизнес-логики и пользовательского интерфейса.
Это обычная архитектура в крупных, достаточно корпоративных проектах. Вы будете иметь картографы, которые переводят туда и сюда эти два вида объектов.
источник
BookRepository
иUserRepository
. Вы всегда будете вызыватьmyRepository.GetById(...)
или делать что-то подобное, и хранилище либо создаст объект и загрузит его значения из хранилища данных, либо получит его из кэша. Кроме того, дочерние объекты в основном загружаются с отложенной загрузкой, что также предотвращает необходимость иметь дело с прямыми циклическими ссылками во время построения.