Паттерн Value Object описывает способ представления данных, при котором объект определяется не своей идентичностью, а совокупностью значений. В такой модели важна не ссылка на объект, а его содержимое. Два экземпляра считаются равными, если совпадают все их поля, независимо от того, являются ли они одним и тем же экземпляром в памяти.
Ключевые характеристики Value Object:
Такой подход особенно важен в доменах, где данные описывают состояние, а не сущность с жизненным циклом. Временные типы, денежные значения, координаты и диапазоны естественно ложатся в эту модель.
В доменной модели различие между Entity и Value Object определяется способом идентификации.
Entity:
Value Object:
В контексте работы со временем идентичность теряет смысл. Дата «2026-05-25» не отличается как сущность в разных частях программы. Она либо равна другой дате, либо нет.
Иммутабельность является фундаментом Value Object подхода. Объект после создания не изменяет внутреннее состояние.
Характерные свойства:
Это устраняет класс проблем:
Иммутабельность делает поведение предсказуемым, особенно в многопоточном и асинхронном коде.
js-joda реализует модель работы с датой и временем, основанную на принципах java.time и строгом Value Object подходе.
Основная идея библиотеки заключается в том, что все временные типы являются неизменяемыми объектами-значениями. Каждый экземпляр представляет конкретное значение времени без скрытого состояния.
LocalDate представляет календарную дату без времени и временной зоны.
Каждое значение является самостоятельным объектом:
import { LocalDate } from '@js-joda/core';
const d1 = LocalDate.of(2026, 5, 25);
const d2 = LocalDate.of(2026, 5, 25);
d1 и d2 равны по значению, несмотря на
разные ссылки.
LocalTime описывает время суток без даты:
Модель также полностью иммутабельна.
import { LocalTime } from '@js-joda/core';
const t1 = LocalTime.of(10, 30);
const t2 = LocalTime.of(10, 30);
LocalDateTime объединяет дату и время без зоны:
import { LocalDateTime } from '@js-joda/core';
const dt = LocalDateTime.of(2026, 5, 25, 10, 30);
Значение не связано с конкретным моментом времени в мире, а описывает локальное представление.
Instant представляет точку на временной оси в UTC.
import { Instant } from '@js-joda/core';
const i1 = Instant.now();
ZonedDateTime объединяет локальную дату-время с временной зоной:
import { ZonedDateTime, ZoneId } from '@js-joda/core';
const zdt = ZonedDateTime.now(ZoneId.of('Europe/Paris'));
ZoneId сам по себе также является Value Object:
Оба типа представляют интервалы, но на разных уровнях абстракции.
Duration — машинное время (секунды, наносекунды):
import { Duration } from '@js-joda/core';
const d = Duration.ofMinutes(90);
Period — календарные интервалы:
import { Period } from '@js-joda/core';
const p = Period.ofDays(10);
Value Object обязан корректно реализовывать семантику равенства.
В js-joda сравнение выполняется через методы:
d1.equals(d2)
или через:
d1.isEqual(d2)
Сравнение основано исключительно на значениях полей объекта.
Пример логики:
При совпадении всех компонентов объекты считаются равными.
Любая операция над временными объектами возвращает новый экземпляр.
const date = LocalDate.of(2026, 5, 25);
const next = date.plusDays(1);
Исходный объект остаётся неизменным. Это принципиально отличает модель от изменяемых структур.
Аналогично:
Value Object корректно работает в структурах данных, основанных на равенстве.
Особенность JavaScript требует осторожности, поскольку стандартные коллекции используют ссылочную семантику:
Поэтому Value Object используется как вычисляемое значение, а не как идентификатор.
Пример:
const set = new Set();
set.add(LocalDate.of(2026, 5, 25));
set.add(LocalDate.of(2026, 5, 25));
В результате могут присутствовать два элемента, так как ссылки различны, несмотря на равенство по значению.
Операции js-joda строятся как чистые функции:
Пример цепочки:
const result = LocalDate
.of(2026, 1, 1)
.plusMonths(2)
.plusDays(10)
.minusWeeks(1);
Каждый шаг создаёт новый Value Object.
Некоторые Value Object поддерживают упорядочивание через compareTo:
d1.compareTo(d2)
Результат:
Это позволяет использовать объекты в сортировке:
const sorted = dates.sort((a, b) => a.compareTo(b));
Value Object модель обеспечивает предсказуемость:
В js-joda это особенно важно для:
В доменных системах временные типы часто становятся фундаментом модели:
Использование Value Object устраняет необходимость ручного контроля состояния и синхронизации значений между слоями приложения.
Вместо изменения состояния используется преобразование:
Каждое преобразование возвращает новый Value Object, сохраняя исходное значение неизменным, что делает модель линейной и легко анализируемой по цепочке операций.