"Value Objects and Immutability"


S1 E2

 

What is "Better code saul" aboUt

The vision of "Better Code Saul" is to advocate good code practices and good coding design techniques in order to inspire others to adopt them and increment software's code quality.

 

The season one's mission of "Better Code Saul" is to provide a short-term benefit to your code quality.


The episodes succession is meant to be of incremental difficulty and each episode is useful for the following.

PRACTICAL EXAMPLE

A Value Object is a data type that can differ one another by their properties' values. The VO concept differs from Entity concept.

Two entities can have the same properties but they have a unique identifier which make them unique anyway.
Two VOs that have the same properties are equal.

 

A Value Object must also be immutable. A VO that is mutable is not a VO :)

 

The concept of Value Object came along with Eric Evan's DDD Book "Domain-Driven Design: Tackling Complexity in the Heart of Software"

"Objects that are equal due to the value of their properties [...] are called value objects." - M. Fowler (source)

What is a "value object"

What is a "value object"

PRACTICAL EXAMPLE

In his book, Eric Evans gives us a practical example on the identity matter:  

A child is drawing using a crayon of a certain color.

The focus is on the drawing, not on the crayon being used.
As long as the color used is the same, the child can decide to change the crayon without any concern.

CODE EXAMPLE

What is a "value object"

CODE EXAMPLE

<?php

use Assert\Assertion;

final class IdentityCard {
  private string $code;
  private bool $isValid;
    
  public function __construct(string $code, bool $isValid) {
    $this->code = $code;
    $this->isValid = $isValid;
  }
}

$id1 = new IdentityCard('123', true);
$id2 = new IdentityCard('123', true);

Assertion::eq($id1, $id2); // True assertion


$id1 = new IdentityCard('123', true);
$id2 = new IdentityCard('321', true);

Assertion::eq($id1, $id2); // False assertion.

WHEN THEY ARE USEFUL

If you remember rule 3 of S1 E1 of Better Code Saul, Object Calisthenics, you will see that the point is "wrapping primitives data when they have some behaviour"

 

Since this is a principle, we could apply it here as well. We can and should use VOs in our code base to express something meaningful of our domain.

Using VOs as types also helps us to get rid of a very common code smell: Primitive Obsession.

Remember: Writing good code is never an overkill for any code base ;)

usage principle

A VO can self-validate everywhere. Consider the following example.

WHEN THEY ARE USEFUL

ADVANTAGES

<?php

// Function i need to use somewhere :)
function getEmail(array $payload): string {
  return $this->payload['email'];
}

Am I sure the given email address is valid? I could be sure. There could be a validator somewhere, maybe a framework's validator of my request. But what if we keep it more simple and testable?

Now I can benefit the validation anywhere and I can simply unit test this class.
Moreover, I can model my VO on the behaviour of the data its wrapping.

WHEN THEY ARE USEFUL

ADVANTAGES

<?php

// VO definition
final class EmailAddress {
  private string $emailAddress;
  
  public function __construct(string $emailAddress)
  {
    if (! $this->isValidEmail($emailAddress)) {
      throw new \Exception('WRONG, MY MAN');
    }
  
    $this->emailAddress = $emailAddress;
  }
  
  private function isValidEmail(string $emailAddress): void {
    // implementation
  }
}

WHEN THEY ARE USEFUL

ADVANTAGES

<?php

// Function i need to use somewhere :)
function getEmail(array $payload): EmailAddress {
  return new EmailAddress($this->payload['email']);
}
<?php

// Function i need to use somewhere :)
function getEmail(array $payload): string {
  return $this->payload['email'];
}

Before

After

As already being said, a VO is immutable.

WHEN THEY ARE USEFUL

MORE ADVANTAGES

<?php

final class IdentityCard {
  private string $code;
  private bool $isValid;
    
  public function __construct(string $code, bool $isValid) {
    $this->code = $code;
    $this->isValid = $isValid;
  }
  
  public function getCode(): string {
  	return $this->code;
  }
  
  public funciton isValid(): bool {
    return $this->isValid;
  }
}

No setters of any sort. Private properties.
The only initialization point should be the constructor.

We get rid of any Aliasing Bug.

WHEN THEY ARE USEFUL

MORE ADVANTAGES

Date retirementDate = new Date(Date.parse("Tue 1 Nov 2016"));

// this means we need a retirement party
Date partyDate = retirementDate;

// but that date is a Tuesday, let's party on the weekend
partyDate.setDate(5);

assertEquals(new Date(Date.parse("Sat 5 Nov 2016")), retirementDate);
// oops, now I have to work three more days :-(

Take a look at the following code

Not using an immutable object leads us to the Alias Bug problem, where we access the same memory location through more than one reference, thus unintentionally changing values of many objects.

Imagine how bad this can be if you change many product delivery or many bank transfers data at once :(

WRAPPING ALL UP

Using VOs will lead us into writing code that is:

  1. Better typed
  2. Immutable
  3. Self-validating
  4. Safer and less bug-prone

 

At the very end, we are eating pieces of DDD concepts. We will see again some of these concepts in the latest(s) episode(s) of Better Code Saul.


The point is adopting techniques that will enable us to tackle our domain complexity better for the future of our codebase

RESOURCES

THANK YOU

Better Code Saul S1E2: Value Objects and Immutability

By Kevin Cittadini

Better Code Saul S1E2: Value Objects and Immutability

  • 265