Value objects in DDD - Why immutable?
Asked Answered
P

7

45

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?

Parasang answered 3/1, 2011 at 1:55 Comment(4)
Value and reference types are generally intermixed as shown in your original question when speaking about Address (struct) value type and String (class) property value reference type. Is there any advantage to restrict your question to just value types? Can you open it up without diluting your intent?Sepulveda
I'm not sure I follow. I'm not talking about CLR value types (structs, ints, etc) vs reference types (classes, etc). I'm talking about the DDD concept of Value Objects (as opposed to Entity Objects).Parasang
I scanned the question, saw C# and my mind jumped contexts. Thanks for the clarification.Sepulveda
Value objects need not actually be immutable to avoid aliasing problems. Just pass them by values instead of by reference and you will be fine. See wiki.c2.com/?ValueObjectsCanBeMutable.Endowment
G
62

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.

Gateshead answered 3/1, 2011 at 7:33 Comment(10)
This argument is flawed. You are arguing from a technical perspective for something that is chosen for conceptual reasons. These concepts of Value Object and Entity exist outside of any specific implementation.Hoptoad
It covers why we need immutable value objects, no matter if you use a specific technology or whatever, the weight 75kg is immutable no matter if you use EF4 or any other technology. if someones weight has changed, it is not the object representing 75kg that should change, you should assign a new weight to that person instead..Gateshead
Only throwing the theoretical aspects around will not quite show why you get in trouble if you ignore those. IMO, it is easier to get why the concepts exist if you can see why something would fail in a more concrete scenario.Gateshead
+1 to @Roger. While the example link I've provided talks about the same thing, the explanation is not as explicit as you have mentionedQuondam
@Roger Alsing: Your argument just says why mutability can sometimes get you in trouble. It doesn't argue why Value Objects should be immutable. The fact that you never mentioned the definition of Value Objects should be a clue that something is amiss.Hoptoad
+1. Answered the question with a great example. Similar to Martin Fowler's explanation (c2.com/cgi/wiki?ValueObjectsShouldBeImmutable).Bac
Fundamentally, I think the issue is that a data holder will behave as a value if either it can never change, or if there only exists one reference to it, but it will behave as an entity if code which holds a reference to it can see it change outside the control of that code. To behave as values, data holders must either be unshared or immutable. Since value objects are supposed to be sharable, that implies they must be immutable.Brinn
In that context immutability is important in connection with the conception of ValueObjects.To wit cause both Jimmy and Joe rely on some idea of 75 kg and they are not interested in knowing is the 75kg object "unique" or not (ValueObjects areDefined byTheirAttributes, atTheContrast of entity ones). IOW they need to denote their weight of 75 kg and don't get any specific urge to have a relationship with the 75kg obj. So it can be confusable when the 75kg changes its essential being. I think it is even more notable than the fact we've set new Weight.Value with Jimmy's help, covering it from Joe.Valina
Value objects need not actually be immutable to avoid aliasing problems. Just pass them by values instead of by reference and you will be fine. See wiki.c2.com/?ValueObjectsCanBeMutable.Endowment
thanks for such wonderful mental model and explanation !! can you (or point me to) similar kind of understanding for entity objects in context of DDD ?Liebig
H
46

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.

Hoptoad answered 3/1, 2011 at 2:13 Comment(8)
I've seen examples like this, and they seem too simplistic. 6 is immutable because, like String, any operation you perform on it gives you a new object. 6+2 is not the number six that has been changed to eight; It's a new number. I get that, but I don't like the analogy. In the case of Address, the type is more complex and it is more difficult (and feels silly) to define operations on it that return a new type. For example, newAddress = oldAddress.ChangeLine1("123 Main Street")?Parasang
You're not thinking about this carefully enough. You could create a type that mimics int that does modify the object when you invoke an instance-level method on it (call the class Integer and as an example, an instance-level method Increase that accepts a parameter of type Integer). Why don't we do that? Why do we model numbers as Value Objects?Hoptoad
Gosh, I don't know. It wouldn't even occur to me to do otherwise =) Maybe give me a hint?Parasang
@Hobbes: What if int was mutable? What happens here? 6.Increase(1); Console.WriteLine(6); Does that jive with your intuition as to what an int is, or is it completely contrary to your intuition as to what an int is? Should you really be able to change what 6 represents?Hoptoad
Well, six is always six. Intuitively, Console.WriteLine(6) should always be "6". This example is straightforward enough, but I am having a hard time applying it back to the Address idea. I'm getting the sense from you that there is something dangerous about saying jason.Address.Line1 = "123 Main" and that it's better to say jason.Address = new Address(...). I still don't know why.Parasang
@Hobbes: Okay, but why is 6 always 6? It's 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.Hoptoad
Last comment nails it perfectly.Irmairme
@Hobbes: Suppose one says jason.Address = fred.Address; jason.Address.Line1 = "123 Main"; What effect, if any, should that have on fred.Address? Should it leave free.Address unaffected? For some types like Address, a struct with exposed fields may offer the most logical semantics, with every variable or field of its type being "detached" from every other (and the fact that it's detached being readily apparent by the fact that it's a struct with exposed fields).Brinn
C
16

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.

Clostridium answered 8/2, 2011 at 8:32 Comment(3)
Thank you for this. Evan's explanation is enlightening and your example really drives it home.Stylopodium
This is by far the simplest and best answer. It uses the example from the question, and shows why it would fail without being a VO.Attemper
why you did not had: var fullname1 = new FullName { FirstName = "Alice", LastName = "Jones" }; and var fullname2 = new FullName { FirstName = "Alice", LastName = "Jones" }; in first place to not get into this issue ?Liebig
Q
5

This might not be the complete answer. I am only answering your question on the advantages of Immutability.

  1. Because Immutable objects are thread safe. Since they cannot change state, they cannot be corrupted by thread interference or observed in an inconsistent state.
  2. The references to the immutable objects can be easily shared or cached without having to copy or clone them as their state can not be changed ever after construction.
  3. For more advantages of Immutability you look here (LBushkin's answer) What's the advantage of a String being Immutable?

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.

Quondam answered 3/1, 2011 at 2:4 Comment(6)
Any argument that comes at this from a technical perspective is wrong. You never once used the fact that we are talking about Value Objects here. Your analogy to string is flawed since string is a reference type. Your argument is really about the benefits of immutable types, not why Value Objects should be immutableHoptoad
Isn't the Value Object implied here. Moreover the question asked is "can someone explain why immutability is a Good Thing". Now coming to the link I've provided; instead of copying the advantages verbatim, I specified the link which talks about advantages of Immutability. So can you enlighten me on why Address is not a reference type.Quondam
Pangea: Good point. I asked "why immutability is a Good Thing," but what I really meant is "why is immutability a Good Thing" for value objects? I will edit my question to make it more clear.Parasang
I've read the c2.com link you posted. I think that they are not talking about Domain Driven Design?Parasang
@Parasang - I've updated my answer. Look at the last paragraph. Hope this clears some stuff.Quondam
I'd rephrases @Pangea 's argument as this: using immutable objects make it easier to write a correct program in most cases where they are practical (considering the PL and other technologies of your choice) and value object is a good match for them. And it's a valid point that it's not mandatory to make VO immutable.Stanwood
D
2

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.

Drawn answered 3/11, 2016 at 14:52 Comment(0)
B
2

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;
    }
}
Burin answered 1/8, 2018 at 21:0 Comment(0)
M
0

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:

  1. They have no identity, i.e. they are represented by their state (explained in the cash example above)
  2. Equality: Two ValueObjects with same property values should be equal.
  3. Immutability: If any property value needs to be changed, a new instance must be created.

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 Employees 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;}
}

Immutability problem

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)

Equality problem

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.

Solution

Now let's reconsider the above 2 problems with C# record SalaryStructure, Employee remains class.

public record SalaryStructure(decimal BasicSalary, decimal Allowance);

Immutability problem

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.

Equality problem

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.

Montagna answered 13/6 at 22:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.