Разница между сущностями (entity) и значениями (value) в объектно-реляционном отображении

При работе с Hibernate и другими ОРМ принято придерживаться принципа глубокой детализации предметной области (fine-grained domain model), что обычно означает, что классов с данными в приложении должно быть больше, чем таблиц в базе данных. Это достигается за счёт разделения классов на сущности и значения.

Представим следующий класс для работы с данными:

В базе ему соответствует таблица USERS:

IDNAMEADDRESSBIRTHDAYEMAIL
1Иванов Иван Ивановичг. Москва, ул. Садовая, д.1, кв.21980-01-01ivanov@ivanov.com

Совершенно очевидно, что класс предметной области User содержит данные в несколько «скомканном» виде. Во-первых, имя пользователя надо разделить на ФИО. Если мы будем отправлять пользователю письмо, то обращаться мы к нему должны по имени-отчеству, без фамилии. Во-вторых, адрес тоже надо бы разбить как минимум на город и всё остальное.

Проведём рефакторинг, выделив два новых класса:

Мы глубже детализировали предметную область. Теперь нам надо определиться с тем, как зарефакторить таблицы базы данных. Для этого нам нужно определить, какие из этих трёх классов являются полноценными сущностями, а какие — значениями.

Сущности (entities, на примере класса User):

  • Обязательно имеют свой идентификатор (свой ID в базе). Класс User без сомнений должен иметь свой ID и свою таблицу в базе.
  • Имеют собственный жизненный цикл, ни к чему не привязаны. То есть создаются и удаляются, не зависимо от других сущностей. Когда в системе регистрируется новый пользователь, то появляется строка в таблице, а при выборке новый объект класса User в приложении. И не важно существуют ли какие-то другие записи в таблицах и объекты в приложении.
  • На одну и ту же сущность могут ссылаться другие сущности (как в базе, так и в приложении). Если, например, в предметной области есть понятие «статей» с классом Article (и таблицей ARTICLES) и у них есть поле User author, которое ссылается на пользователя, то несколько статей могут ссылаться на один и тот же объект User автора.

Значения (values, на примере класса Name):

  • Может не иметь своего идентификатора в базе, может не иметь своей таблицы. Данные из полей класса Name могут храниться в соответствующих полях таблицы USERS.
  • Объекты класса Name не имеют собственного жизненного цикла. Когда в приложении создаётся объект класса User для него создаётся объект класса Name для хранения данных об имени пользователя. Когда этот объект класса User удаляется из памяти, то и соответствующий Name встаёт в очередь к сборщику мусора.
  • Даже если у пользователей имена совпадают, то для каждого объекта класса User создаётся свой объект класса Name. Не может быть такого, чтобы два разных объекта User ссылались на один и тот же объект Name.

С классом Address можно поступить как угодно. В данном контексте его можно считать и значением, то есть полностью зависимым от User. Можно считать и самостоятельной сущностью. Особенно, если учесть тот факт, что два пользователя могут проживать по одному и тому же адресу. А значит адрес является единой сущностью (а не просто совпадением, как в именах) не только в модели, но и в реальной жизни.

Исходя из этих соображений базу данных можно переделать на две таблицы:

USERS:

IDFIRSTNAMEMIDDLE_NAMELASTNAMEADDRESS_IDBIRTHDAYEMAIL
1ИванИвановичИванов11980-01-01ivanov@ivanov.com

ADDRESSES:

IDCITYLOCATION
1Москваул. Садовая, д.1, кв.2

Сказанное выше вовсе не означает, что у ФИО не может быть отдельной таблицы со своим ID. Но в общем случае, когда модель предметной области отражает что-то более-менее реальное, получается, что количество таблиц меньше, чем количество классов, так как некоторые классы считаются не сущностями, а значениями, и хранят свои данные в таблицах сущностей, от которых они зависят. А собственных таблиц, соответственно, не имеют.

Также важно отметить, что если мы признаём какой-то класс значением, а не сущностью, то мы должны не забыть так или иначе увязать жизненный цикл этого значения с какой-то сущностью, от которой значение будет зависеть. То же касается данных в базе (каскадные удаления и т.п.), особенно если для значения всё-таки делалась отдельная таблица.