Объекты значения — базовый элемент организации логики
В предыдущих постах мы разобрали подходы к организации бизнес-логики — Transactional Script, Active Record. Теперь начнем движение в сторону полноценной Domain Model. Но сначала хочется подробнее остановиться на одном из фундаментальных строительных блоков доменной модели — Value Object. Этот паттерн прост по своей сути, но вместе с тем очень часто его неправильно имплементируют или используют.
Концепция Value Object сформировалась в ООП задолго до появления DDD. Еще Мартин Фаулер описывал паттерн для работы с небольшими неизменяемыми объектами. Но как основа и важный строительный блок разработки сложных программных систем была представлена в знаменитой «Синей Книге» Эрика Эванса — Domain-Driven Design: Tackling Complexity in the Heart of Software (2003).
Суть паттерна Value Object заключается в том, что объект описывает некоторую характеристику или атрибут, важный для предметной области, и при этом не обладающий собственной идентичностью. То есть объект определяется значением его свойств.
Например, адрес доставки, денежная сумма, цвет товара — это все значения, а не сущности. Объекты с одинаковыми свойствами взаимозаменимы.
Свойства Value Objects
У Value Object есть определенный набор строгих характеристик:
🔹 Иммутабельность — после создания объект не должен менять свое внутреннее состояние. Любое изменение создает новый объект.
🔹 Отсутствие идентичности — Value Object полностью определяется своими атрибутами. Два объекта равны, если равны их атрибуты.
🔹 Инвариантность — содержит всю необходимую логику для работы со своими атрибутами: валидация, вычисления, преобразования.
🔹 Отсутствие побочных эффектов — любое изменение атрибута должно создавать новый экземпляр объекта вместо изменения текущего.
В чем отличие от Domain Entity
Самый частый вопрос при проектировании — как понять, что перед вами Value Object, а не Domain Entity?
Ключевое отличие заключается в наличии идентичности (Identity) и жизненного цикла.
- Domain Entity имеет уникальный идентификатор (ID). Сущность проживает определенный жизненный цикл: она создается, меняет свои состояния с течением времени и удаляется. Нам критически важно отличать одну сущность от другой, даже если у них совпадают атрибуты.
- Value Object не имеет идентификатора. У него нет истории изменений. Это просто слепок данных в конкретный момент времени.
Правило большого пальца: Всегда старайтесь моделировать концепцию как Value Object. Переходите к Entity только тогда, когда абсолютно необходимо отслеживать жизненный цикл этого объекта и отличать его от других таких же по уникальному ключу.
Как реализовывать Value Objects
Основные нюансы реализации Value Object:
✅ Делайте Value Objects иммутабельными.
✅ Переопределяйте equals(), hashCode() (если пишете на Java).
✅ Валидацию производите в конструкторе или выносите в фабричный метод.
✅ Избегайте прямых модификаций полей (не используйте setXxx(...)) — используйте withXxx() для создания новых экземпляров.
Пример реализации на Java с использованием Record:
Выводы
- Value Objects — это основные строительные блоки доменной модели.
- Value Objects обладают собственным поведением и инвариантами.
- Они помогают повысить безопасность и выразительность кода.
Не бойтесь создавать много маленьких объектов значений — это инвестиция в читаемость, выразительность и надёжность вашей системы.
Подпишись на мой канал в telegram