DEV Community

Mo
Mo

Posted on

Value Object

What is a value object?

A value object is a design pattern that represents an immutable object with equality based on its state, not its identity. This means that two value objects are equal if they have the same properties and values, regardless of their references. For example, two money objects with the same amount and currency are equal, even if they are different instances.

A value object has the following characteristics:

  • It is immutable, meaning that its state cannot be changed after creation
  • It has no identity, meaning that it does not have a unique identifier or a reference to another object
  • It is self-contained, meaning that it does not depend on external resources or services
  • It is comparable, meaning that it can be compared with other value objects of the same type

How to implement a value object base class in C#?

One way to implement a value object in C# is to create a base class that overrides the Equals and GetHashCode methods, and provides a copy constructor. The Equals method compares two value objects by calling GetAtomicValues method to compare their values. The GetHashCode method returns a hash code based on the values of the public properties.

Here is an example of a value object base class in C#:

public abstract class ValueObject : IEquatable<ValueObject>
{    
    public abstract IEnumerable<object> GetAtomicValues();

    public bool Equals(ValueObject? other)
    {
        return other is not null && ValueObjectsAreEqual(other);
    }

    public override bool Equals(object? obj)
    {
        return obj is not null && obj is ValueObject other && ValueObjectsAreEqual(other);
    }    

    public override int GetHashCode()
    {
        return GetAtomicValues().Aggregate(default(int), HashCode.Combine);
    }

    private bool ValueObjectsAreEqual(ValueObject other)
    {
        return GetAtomicValues().SequenceEqual(other.GetAtomicValues());
    }
}
Enter fullscreen mode Exit fullscreen mode

Now, you have the option to inherit from the ValueObject class, like:

public sealed class SampleValueObject : ValueObject
{
    public string PropertyOne { get; set; } = "Value1";

    public int PropertyTwo { get; set; } = 2;

    public override IEnumerable<object> GetAtomicValues()
    {
        yield return PropertyOne;
        yield return PropertyTwo;
    }
}
Enter fullscreen mode Exit fullscreen mode

After defining the SampleValueObject class, you can write a sample xUnit unit test for the preceding class:

[Fact]
public void Equals_WhenCalledWithEqualValueObjects_ReturnsTrue()
{
     // Arrange
     var valueObject1 = new SampleValueObject();
     var valueObject2 = new SampleValueObject();

     // Act
     var result = valueObject1.Equals(valueObject2);

     // Assert
     Assert.True(result);
}
Enter fullscreen mode Exit fullscreen mode

How to map Value Object in EF

Entity Framework Core supports value objects through the concept of owned entity types. To map a value object, you need to use the .OwnsOne method in the OnModelCreating method of your DbContext class. This method takes a lambda expression that specifies the navigation property that represents the value object.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<SampleEntity>()
         .OwnsOne(e => e.ValueObject);
}
Enter fullscreen mode Exit fullscreen mode

This will create a table for SampleEntity with columns for the properties of ValueObject. The table name will be the same as the entity name, and the column names will be prefixed with the navigation property name. For example, the table will have columns called ValueObject_PropertyOne and ValueObject_PropertyTwo.

Why use value objects in your code?

Using value objects in your code can improve the readability, maintainability and testability of your code, as well as avoid unnecessary mutations and side effects. Here are some of the benefits of using value objects:

  • Readability: Value objects make your code more expressive and declarative, as they capture the meaning and behavior of the domain concepts. For example, a money value object can encapsulate the amount and currency, and provide methods for arithmetic operations and formatting.
  • Maintainability: Value objects reduce the complexity and duplication of your code, as they encapsulate the logic and validation of the state. For example, a date value object can ensure that the date is valid and provide methods for comparison and manipulation.
  • Testability: Value objects make your code easier to test, as they are self-contained and have no external dependencies. You can use simple equality checks to verify the state and behavior of the value objects, without relying on mocks or stubs.
  • Immutability: Value objects prevent unwanted mutations and side effects, as they cannot be changed after creation. You can safely pass them around without worrying about their state being modified by other parts of the code.

Summery

In summary, value object is a useful design pattern for representing objects that have no identity and are defined by their state. Using value objects in your code can improve the readability, maintainability and testability of your code, as well as avoid unnecessary mutations and side effects.

Please find Nuget Package and GitHub repository links of this project here.

Top comments (0)