Value Objects in .NET (DDD Fundamentals)
Tags: ddd, domaindrivendesign, dotnet, csharp
Published: 2023-12-25 18:17:44

---
title: Value Objects in .NET (DDD Fundamentals)
published: true
date: 2023-12-23 00:00:00 UTC
tags: ddd,domaindrivendesign,dotnet,csharp
canonical_url: https://www.milanjovanovic.tech/blog/value-objects-in-dotnet-ddd-fundamentals
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xkevoopy9k1aap2wm6wt.png
---
**Value Objects** are one of the building blocks of Domain-Driven Design. DDD is a software development approach for solving problems in complex domains.

Value objects encapsulate a set of primitive values and related invariants. A few examples of value objects are money and date range objects. Money consists of an amount and currency. A date range consists of start and end dates.

Today, I'll show you some best practices for implementing Value Objects.

## What are Value Objects?

Let's start with the definition from the Domain-Driven Design book:

> An object that represents a descriptive aspect of the domain with no conceptual identity is called a Value Object. Value Objects are instantiated to represent elements of the design that we care about only for what they are, not who or which they are.

_— [Eric Evans](http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215)_

Value objects are different from entities - they don't have a concept of identity. They encapsulate primitive types in the domain and solve [primitive obsession.](https://refactoring.guru/smells/primitive-obsession)

There are two main qualities of Value Objects:

- They are immutable
- They have no identity

Another quality of value objects is structural equality. Two value objects are equal if their values are the same. This quality is the least important in practice. However, there are cases where you want only some values to determine equality.

## Implementing Value Objects

The most important quality of value objects is immutability. The values of a value object can't change once an object is created. If you want to change an individual value, you need to replace the entire value object.

Here's a `Booking` entity with primitive values representing an address and the start and end dates of the booking.

```csharp
public class Booking
{
    public string Street { get; init; }
    public string City { get; init; }
    public string State { get; init; }
    public string Country { get; init; }
    public string ZipCode { get; init; }

    public DateOnly StartDate { get; init; }
    public DateOnly EndDate { get; init; }
}
```

You can replace these primitive values with `Address` and `DateRange` value objects.

```csharp
public class Booking
{
    public Address Address { get; init; }

    public DateRange Period { get; init; }
}
```

But how do you implement value objects?

### C# Records

You can use C# [records](https://www.milanjovanovic.tech/blog/records-anonymous-types-non-destructive-mutation) to represent value objects. Records are immutable by design, and they have structural equality. We want both of these qualities for our value objects.

For example, you can represent an `Address` value object using a `record` with a [primary constructor.](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/instance-constructors#primary-constructors)The advantage of this approach is conciseness.

```csharp
public record Address(
    string Street,
    string City,
    string State,
    string Country,
    string ZipCode);
```

However, you lose this advantage when defining a private constructor. This will happen when you want to enforce invariants while creating the value object. Another issue with using records is avoiding value object invariants using the `with` expression.

```csharp
public record Address
{
    private Address(
        string street,
        string city,
        string state,
        string country,
        string zipCode)
    {
        Street = street;
        City = city;
        State = state;
        Country = country;
        ZipCode = zipCode;
    }

    public string Street { get; init; }
    public string City { get; init; }
    public string State { get; init; }
    public string Country { get; init; }
    public string ZipCode { get; init; }

    public static Result<Address> Create(
        string street,
        string city,
        string state,
        string country,
        string zipCode)
    {
        // Check if the address is valid

        return new Address(street, city, state, country, zipCode);
    }
}
```

### Base Class

The alternative way to implement value objects is with a `ValueObject` base class. The base class handles structural equality with the `GetAtomicValues` abstract method.`ValueObject` implementations have to implement this method and define the equality components.

The advantage of using a `ValueObject` base class is that it's explicit. It's clear which classes in your domain represent value objects. Another advantage is being able to control the equality components.

Here's a `ValueObject` base class I use in my projects:

```csharp
public abstract class ValueObject : IEquatable<ValueObject>
{
    public static bool operator ==(ValueObject? a, ValueObject? b)
    {
        if (a is null && b is null)
        {
            return true;
        }

        if (a is null || b is null)
        {
            return false;
        }

        return a.Equals(b);
    }

    public static bool operator !=(ValueObject? a, ValueObject? b) =>
        !(a == b);

    public virtual bool Equals(ValueObject? other) =>
        other is not null && ValuesAreEqual(other);

    public override bool Equals(object? obj) =>
        obj is ValueObject valueObject && ValuesAreEqual(valueObject);

    public override int GetHashCode() =>
        GetAtomicValues().Aggregate(
            default(int),
            (hashcode, value) =>
                HashCode.Combine(hashcode, value.GetHashCode()));

    protected abstract IEnumerable<object> GetAtomicValues();

    private bool ValuesAreEqual(ValueObject valueObject) =>
        GetAtomicValues().SequenceEqual(valueObject.GetAtomicValues());
}
```

The `Address` value object implementation would look like this:

```csharp
public sealed class Address : ValueObject
{
    public string Street { get; init; }
    public string City { get; init; }
    public string State { get; init; }
    public string Country { get; init; }
    public string ZipCode { get; init; }

    protected override IEnumerable<object> GetAtomicValues()
    {
        yield return Street;
        yield return City;
        yield return State;
        yield return Country;
        yield return ZipCode;
    }
}
```

## When To Use Value Objects?

I use value objects to solve primitive obsession and encapsulate domain invariants. Encapsulation is an important aspect of any domain model. You shouldn't be able to create a value object in an invalid state.

Value objects also give you type safety. Take a look at this method signature:

```csharp
public interface IPricingService
{
    decimal Calculate(Apartment apartment, DateOnly start, DateOnly end);
}
```

Then, compare it to this method signature, where we added value objects. You can see how the `IPricingService` with value objects is much more explicit. You also get the benefit of type safety. When compiling the code, value objects reduce the chance of errors creeping in.

```csharp
public interface IPricingService
{
    PricingDetails Calculate(Apartment apartment, DateRange period);
}
```

Here are a few more things you should consider to decide if you need value objects:

- **Complexity of invariants** - If enforcing complex invariants, consider using value objects
- **Number of primitives** - Value objects make sense when encapsulating many primitive values
- **Number of duplications** - If you need to enforce invariants only in a few places in the code, you can manage without value objects

## Persisting Value Objects With EF Core

Value objects are part of domain entities, and you need to save them in the database.

I'll show you how to use EF [Owned Types](https://learn.microsoft.com/en-us/ef/core/modeling/owned-entities)and [Complex Types](https://devblogs.microsoft.com/dotnet/announcing-ef8-rc1/#complex-types-as-value-objects)to persist value objects.

### Owned Types

[Owned Types](https://learn.microsoft.com/en-us/ef/core/modeling/owned-entities) can be configured by calling the `OwnsOne` method when configuring the entity. This tells EF to persist the `Address` and `Price` value objects to the same table as the `Apartment` entity. The value objects are represented with additional columns in the `apartments` table.

```csharp
public void Configure(EntityTypeBuilder<Apartment> builder)
{
    builder.ToTable("apartments");

    builder.OwnsOne(property => property.Address);

    builder.OwnsOne(property => property.Price, priceBuilder =>
    {
        priceBuilder.Property(money => money.Currency)
            .HasConversion(
                currency => currency.Code,
                code => Currency.FromCode(code));
    });
}
```

A few more remarks about owned types:

- Owned types have a hidden key value
- No support for optional (nullable) owned types
- Owned collections are supported with `OwnsMany`
- Table splitting allows you to persist owned types separately

### Complex Types

[Complex Types](https://devblogs.microsoft.com/dotnet/announcing-ef8-rc1/#complex-types-as-value-objects) are a new EF feature available in .NET 8. They aren't identified or tracked by a key value. Complex types have to be part of an entity type.

Complex types are more appropriate for representing value objects with EF.

Here's how you can configure an `Address` value object as a complex type:

```csharp
public void Configure(EntityTypeBuilder<Apartment> builder)
{
    builder.ToTable("apartments");

    builder.ComplexProperty(property => property.Address);
}
```

A few limitations for complex types:

- No support for collections
- No support for nullable values

## Takeaway

Value objects help design a rich domain model. You can use them to solve primitive obsession and encapsulate domain invariants. Value objects can reduce errors by preventing the instantiation of invalid domain objects.

You can use a `record` or `ValueObject` base class to represent value objects. This should depend on your specific requirements and the complexity of your domain. I use records by default unless I need some qualities of a `ValueObject` base class. For example, a base class is practical when you want to control equality components.

More learning material about value objects:

- [Solving primitive obsession with value objects](https://youtu.be/P5CRea21R2E)
- [Using EF 8 Complex types for value objects](https://youtu.be/LhCD5CUSP6g)

Hope this was helpful.

See you next week.

* * *

P.S. Whenever you're ready, there are 2 ways I can help you:

1. [**Pragmatic Clean Architecture:**](https://www.milanjovanovic.tech/pragmatic-clean-architecture?utm_source=dev.to&utm_medium=website&utm_campaign=cross-posting) This comprehensive course will teach you the system I use to ship production-ready applications using Clean Architecture. Learn how to apply the best practices of modern software architecture. [**Join 1,700+ students here.**](https://www.milanjovanovic.tech/pragmatic-clean-architecture?utm_source=dev.to&utm_medium=website&utm_campaign=cross-posting)

2. [**Patreon Community:**](https://www.patreon.com/milanjovanovic) Think like a senior software engineer with **access to the source code** I use in my YouTube videos and **exclusive discounts** for my courses. [**Join 950+ engineers here.**](https://www.patreon.com/milanjovanovic)