Problem
How to create a value object (pattern in Domain Driven Design) in C#.
Solution
Create a class that abstracts away (encapsulate) ‘data
’ in your domain and provide methods to work on it. Below is a class I don’t consider a good Value Object:
public enum GenderType { Male, Female }
public sealed class Gender1
{
private readonly GenderType genderType;
internal Gender1(GenderType genderType)
{
this.genderType = genderType;
}
public GenderType GenderType => this.genderType;
}
[Fact]
public void Setting_with_male_returns_Male_for_GenderType_prop()
{
var gender = new Gender1(GenderType.Male);
Assert.Equal(
expected: GenderType.Male,
actual: gender.GenderType);
}
However, the class below is what I do consider a good Value Object:
public sealed class Gender2
{
private readonly GenderType genderType;
internal Gender2(GenderType genderType)
{
this.genderType = genderType;
}
public bool IsMale => this.genderType == GenderType.Male;
public bool IsFemale => this.genderType == GenderType.Female;
}
[Fact]
public void Setting_with_male_enum_returns_true_for_IsMale_prop()
{
var gender = new Gender2(GenderType.Male);
Assert.True(gender.IsMale);
}
Discussion
The difference between the above two Gender
classes is that the first one isn’t encapsulating its internal representation, i.e., how it holds the gender information in an enum
.
Exposing internal state as properties isn’t the same as encapsulating state, although this is a common misunderstanding. Good encapsulation is when the internals (data/behaviour) are abstracted away from the client by providing domain specific behaviour.
The second Gender
class does that by providing IsMale
and IsFemale
properties. This way, if the internal
representation changes, say from enum
to string
, then the client doesn’t need to change/know:
public sealed class Gender3
{
private readonly string genderType;
internal Gender3(string genderType)
{
this.genderType = genderType;
}
public bool IsMale =>
this.genderType.ToUpper() == "MALE" || this.genderType.ToUpper() == "M";
public bool IsFemale =>
this.genderType.ToUpper() == "Female" || this.genderType.ToUpper() == "F";
}
Client code (the Test
in this case) doesn’t need to change even though the internal representation of the class has changed.