Объекты-Значения в .NET. Окончание
Начало
Когда использовать объекты-значения?
Для решения проблемы одержимости примитивами и инкапсуляции инвариантов домена. Инкапсуляция — важный аспект любой модели предметной области. У вас не должно быть возможности создать объект значения в недопустимом состоянии.
Объекты-значения также обеспечивают безопасность типов. Сравните:
public interface IPricingService
{
decimal Calculate(
Apartment apartment,
DateOnly start,
DateOnly end);
}
С реализацией, в которую добавлен объекты-значения:
public interface IPricingService
{
PricingDetails Calculate(
Apartment apartment,
DateRange period);
}
Второй вариант гораздо яснее выражает намерение и снижает вероятность ошибок (например, перепутанных дат).
Однако, если применить инварианты нужно только в нескольких местах кода, лучше обойтись без объектов-значений.
Сохранение объектов-значений с помощью EF Core
Объекты-значения можно сохранять, используя принадлежащие (Owned) и комплексные (Complex) типы EF Core.
Принадлежащие типы можно настроить, вызвав метод OwnsOne при настройке сущности. Это укажет EF сохранять объекты-значений в той же таблице в виде дополнительных столбцов:
public void Configure(
EntityTypeBuilder<Apartment> builder)
{
builder.ToTable("apartments");
builder.OwnsOne(p => p.Address);
builder.OwnsOne(p => p.Price, b =>
{
b.Property(m => m.Currency)
.HasConversion(
curr => curr.Code,
code => Currency.FromCode(code));
});
}
Замечания:
- Принадлежащие типы имеют скрытое значение ключа.
- Нет поддержки необязательных (обнуляемых) принадлежащих типов.
- Поддерживаются принадлежащие коллекции с помощью OwnsMany.
- Разделение таблиц позволяет сохранять принадлежащие типы отдельно.
Комплексные типы
Это новая функция EF, доступная в .NET 8. Они не идентифицируются и не отслеживаются по значению ключа, поэтому лучше подходят для хранения объектов-значений. Комплексные типы должны быть частью типа сущности:
public void Configure(
EntityTypeBuilder<Apartment> builder)
{
builder.ToTable("apartments");
builder.ComplexProperty(p => p.Address);
}
Ограничения для комплексных типов:
- Нет поддержки коллекций.
- Нет поддержки обнуляемых значений.
Итого
Объекты-значения помогают разработать богатую модель предметной области. Вы можете использовать их для решения проблемы одержимости примитивами и инкапсуляции инвариантов предметной области. Объекты-значения могут уменьшить количество ошибок, предотвращая создание недопустимых объектов домена.
Вы можете использовать записи или базовый класс ValueObject для представления объектов-значений. Это должно зависеть от конкретных требований и сложности домена.
Источник: https://www.milanjovanovic.tech/blog/value-objects-in-dotnet-ddd-fundamentals