Reputation: 461
I don't get why value objects in DDD should be immutable, nor do I see how this is easily done. (I'm focusing on C# and Entity Framework, if that matters.)
For example, let's consider the classic Address value object. If you needed to change "123 Main St" to "123 Main Street", why should I need to construct a whole new object instead of saying myCustomer.Address.AddressLine1 = "123 Main Street"? (Even if Entity Framework supported structs, this would still be a problem, wouldn't it?)
I understand (I think) the idea that value objects don't have an identity and are part of a domain object, but can someone explain why immutability is a Good Thing?
EDIT: My final question here really should be "Can someone explain why immutability is a Good Thing as applied to Value Objects?" Sorry for the confusion!
EDIT: To clairfy, I am not asking about CLR value types (vs reference types). I'm asking about the higher level DDD concept of Value Objects.
For example, here is a hack-ish way to implement immutable value types for Entity Framework: http://rogeralsing.com/2009/05/21/entity-framework-4-immutable-value-objects. Basically, he just makes all setters private. Why go through the trouble of doing this?
Upvotes: 46
Views: 17948
Reputation: 21
There are some great answers here. I had been struggling to grasp the concept for a while. This is because we tend to run in circles between Equality and Immutability. Initially I didn't even know this was happening, but from the various answers presented here, now I have a better understanding of why I was failing to understand earlier.
Most answers have addressed the issue in pieces but the complete picture is missing. So this is my attempt to complete the puzzle by fitting the pieces I've found above. But pardon me, this needs to be verbose and will be long.
I want to give credit to all answer's and comments above, they've been most helpful.
I'll start simple by elaborating @jason 6
example which was too simplistic for me and few others. Suppose you distribute 10 cash to all children in a class. Do they have the same amount, yes. Now you give one child 20 cash, now he has 30. Does it mean that everyone has 30 amount, no. Changing one child's cash doesn't mean that the amount others have is also changed. This is immutability. But the 10 cash of the other children have different serial numbers (note's identity). Does this mean they have different amount? No, because the value of the notes is same, this is equality.
If this is enough for you, good enough. But if you're still having trouble relating it with actual programming concepts, read on to find out about how we run in circles when trying to understand the concept and eventually, why we need immutability.
There are 3 aspects to ValueObjects:
We're running in circles between the Immutability problem and the Equality problem. So let's focus on each one by one.
Consider the following Employee
s and their SalaryStructure
:
public class SalaryStructure
{
public decimal BasicSalary {get;set;}
public decimal Allowance {get;set;}
}
public class Employee
{
// name, age ...
public SalaryStructure SalaryStructure {get;set;}
}
Let's say I want to create a few employees who have the SAME SalaryStructure
. It feels natural to create a common SalaryStructure
instance and assign it to all employees.
void Main
{
SalaryStructure ss = new() { BasicSalary = 5000, Allowance = 500 };
Employee john = new() { SalaryStructure = ss };
Employee max = new() { SalaryStructure = ss };
// ... more employees
bool bothHaveSameSalaryStructure = john.SalaryStructure == max.SalaryStructure; // IT IS
}
As said above that both employees should have SAME salary structure and it is. Equality of VO is intact, so where's the problem?
The problem lies here. After creating various employees, you decide to enhance the Allowance
of the best performer.
...
bool bothHaveSameSalaryStructure = john.SalaryStructure == max.SalaryStructure; // IT IS
john.SalaryStructure.Allowance = 1000;
bothHaveSameAllowance = john.SalaryStructure.Allowance == max.SalaryStructure.Allowance; // it is BUT IT SHOULDN'T BE
Not only Max but all employees' Allowance
was enhanced (MUTATED). Although, Equality is still maintained because they refer to the same SalaryStructure
instance but this is an IMMUTABILITY problem.
THEY ARE EQUAL WHEN THEY SHOULDN'T BE! (remember to look at this statement later, you'll know when)
We can fix the above problem by creating a new instance of SalaryStructure
for each employee, although it is repetitive and seems unnatural to be creating new instances with same values.
void Main
{
Employee john = new() {
new SalaryStructure() { BasicSalary = 5000, Allowance = 500 }
};
Employee max = new() {
new SalaryStructure() { BasicSalary = 5000, Allowance = 500 }
};
bool bothHaveSameSalaryStructure = john.SalaryStructure == max.SalaryStructure; // IT IS NOT, NEW PROBLEM
john.SalaryStructure.Allowance = 1000;
bothHaveSameAllowance = john.SalaryStructure.Allowance == max.SalaryStructure.Allowance; // it is not, PREVIOUS PROBLEM SOLVED
}
This has created a new problem. Before John's allowance enhancement, although both employees have same SalaryStructure
, equality check fails. Although we have solved the previous problem of values being MUTATED to other employees, now have encountered EQUALITY problem.
THEY ARE NOT EQUAL WHEN THEY SHOULD BE! (I told you you'll know when)
And here's the vicious circle. If you begin this whole example from this Equality problem, you will end up with the Immutability problem, back where you started. TRY IT.
Now let's reconsider the above 2 problems with C# record SalaryStructure
, Employee
remains class
.
public record SalaryStructure(decimal BasicSalary, decimal Allowance);
Using same code from above
void Main
{
SalaryStructure ss = new(5000, 500);
Employee john = new() { SalaryStructure = ss };
Employee max = new() { SalaryStructure = ss };
// ... more employees
bool bothHaveSameSalaryStructure = john.SalaryStructure == max.SalaryStructure; // IT IS
}
Now John gets his enhancement, the mutation:
...
bool bothHaveSameSalaryStructure = john.SalaryStructure == max.SalaryStructure; // IT IS
john.SalaryStructure.Allowance = 1000; // compiler error, Allowance is an init property and cannot be mutated. Therefore we must create a new object
john.SalaryStructure = new(5000, 1000);
bothHaveSameAllowance = john.SalaryStructure.Allowance == max.SalaryStructure.Allowance; // IT SHOULDN'T BE and it is not
Immutability solved! And it seems more natural assigning the same SalaryStructure
to all the employees because it is same for all.
Even if you do the unnatural thing by creating new SalaryStructure
instances:
void Main
{
Employee john = new() {
new SalaryStructure(5000,500)
};
Employee max = new() {
new SalaryStructure(5000, 500)
};
bool bothHaveSameSalaryStructure = john.SalaryStructure == max.SalaryStructure; // IT IS like it should have been
// And as for John's enhancement
//john.SalaryStructure.Allowance = 1000;
john.SalaryStructure = new(5000, 1000);
bothHaveSameAllowance = john.SalaryStructure.Allowance == max.SalaryStructure.Allowance; // nope, John's still the best
}
This way, if we use the same instance, the objects are equal, if we create different instances, the objects are still same owing to equality by value and not by reference (like in class), and if we need to change a value, instead of allowing to change single value (the compiler error above), force to change the whole object reference by creating a new instance altogether so that other's using the object are not affected.
Upvotes: 0
Reputation: 23214
Ignore all the crazy answers about thread safe etc, that has nothing to do with DDD. (I've yet to see a thread safe O/R mapper or other DDD friendly dal)
Imagine a value object for weights. lets say we have a KG value object.
sample (edited for clarity):
var kg75 = new Weight(75);
joe.Weight = kg75;
jimmy.Weight = kg75;
Now what would happen if we do:
jimmy.Weight.Value = 82;
That would change the weight of joe too, if we are still using the same object references that is. Note that we assigned an object representing 75kg to both joe and jimmy. When jimmy gains weight, it is not the kg75 object that has changed, it is jimmys weight that has changed, thus, we should create a new object representing 82 kg.
But what if we have a new session and load both joe and jimmy in a clean UoW?
var joe = context.People.Where(p => p.Name = "joe").First();
var jimmy = context.People.Where(p => p.Name = "jimmy").First();
jimmy.Weight.Value = 82;
What would happen then? well, since EF4 in your case would load joe and jimmy and their weights without any identity , we would get two different weight objects and when we change jimmys weight , joe would still weigh the same as before.
So we would have two different behaviours for the same code. If the object references are still the same, then both joe and jimmy would get a new weight. If joe and jimmy are loaded in a clean uow, only one of them would be affected by the change.
And that would be quite incosistent imo.
By using immutable VO's, you would get the same behavior in both cases and you can still reuse object references for a smaller memory footprint when constructing object graphs.
Upvotes: 63
Reputation: 2018
This has been asked a long time ago but I've decided to provide an answer with an example which I find straightforward and very simple to remember. Besides, SO works as a reference for many developers and I think anyone who bumps into this question could benefit from it.
Because they are defined by their attributes, value objects are treated as immutable.
A good example of a value object is money. It doesn't matter that you can't distinguish between the same five one dollar bills in your pocket. You don't care about the currency's identity -only about its value and what it represents. If somebody swapped a five dollar bill for one you have in your wallet, it would not change the fact that you still have five dollars.
So, for example, in C#, you define money as a immutable value object:
public class Money
{
protected readonly decimal Value;
public Money(decimal value)
{
Value = value;
}
public Money Add(Money money)
{
return new Money(Value + money.Value);
}
// ...
// Equality (operators, Equals etc) overrides (here or in a Value Object Base class).
// For example:
public override bool Equals(object obj)
{
return Equals(obj as Money);
}
public bool Equals(Money money)
{
if (money == null) return false;
return money.Value == Value;
}
}
Upvotes: 2
Reputation: 562
Value objects need to be immutable.
Immutable objects do indeed make life simpler in many cases. ... And they can make concurrent programming way safer and cleaner for more info
Let's consider value object to be mutable.
class Name{
string firstName,middleName,lastName
....
setters/getters
}
Let's say your original Name was Richard Thomas Cook
Now let's say you change only the firstName(to Martin) and lastName(to Bond), If it's not an immutable object, you will use the methods to mutate the state one by one. Chances of having name as Martin Thomas Cook in that Aggregate state before the final name of Martin Thomas Bond is never acceptable(also it gives a wrong thinking to the one who looks code later,leading to undesirable dominoes effect in further design).
Mutable value objects explicitly have to enforce integrity constraints for the changes given in 1 transaction,which is given free in Immutable objects. Hence it makes sense to make value objects immutable.
Upvotes: 2
Reputation: 2038
I'm very late to the party, but I'd been wondering about this myself. (Would appreciate any comments.)
I don't think it's been explicitly quoted here, but I think Evans' references to immutability were primarily in the context of sharing:
in order for an object to be shared safely, it must be immutable: it cannot be changed except by full replacement. (Evans p100)
There's also a sidebar in Evan's book called "Is Address a Value Object? Who's asking?".
If roommates each called to order electrical service [ie if two customers had the same address], the company would need to realize it. [So] Address is an Entity. (Evans p98)
In the example you gave, suppose the customer's Home and Business Address were both 123 Main Street. When you make the correction you describe, would both addresses change? If so, and if I'm reading Evans correctly, it sounds like you really have an Entity.
Taking a different example, suppose we had an object to represent a customer's full name:
public class FullName
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Customer
{
public FullName Name { get; set; }
}
Without a value object, the following would fail:
[Test]
public void SomeTest() {
var fullname = new FullName { FirstName = "Alice", LastName = "Jones" };
var customer1 = new Customer { Name = fullname };
var customer2 = new Customer { Name = fullname };
// Customer 1 gets married.
customer1.Name.LastName = "Smith";
// Presumably Customer 2 shouldn't get their name changed.
// However the following will fail.
Assert.AreEqual("Jones", customer2.Name.LastName);
}
In terms of advantages generally, some are picked up at In DDD, what are the actual advantages of value objects?. Notably, you only have to validate a VO on creation. If you do that, then you know it's always valid.
Upvotes: 16
Reputation: 241585
Why is 6 immutable?
Understand that, and you will understand why Value Objects should be immutable.
Edit: I'll lift our dialog into this answer now.
6
is immutable because 6
's identity is determined by what it represents, namely the state of having six of something. You can't change that 6
represents that. Now, this is the fundamental concept of Value Objects. Their value is determined by their state. An Entity, however, is not determined by its state. A Customer
can change their last name, or their address and still be the same Customer
. This is why Value Objects should be immutable. Their state determines their identity; if their states changes, their identity should change.
Upvotes: 48
Reputation: 80166
This might not be the complete answer. I am only answering your question on the advantages of Immutability.
This is an example by Martin Fowler on why value objects should be Immutable.
Alright, while it is not mandatory to make VO as immutable (even DDD book doesn't say they have to be immutable), the main idea, in DDD, to make it a VO seems to be not to deal with life cycle complexities like that of an Entity. Look here for more details.
Upvotes: 5