Kevin Cittadini
Software Engineer
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)
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
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.
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 ;)
A VO can self-validate everywhere. Consider the following example.
<?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.
<?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
}
}
<?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.
<?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.
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 :(
Using VOs will lead us into writing code that is:
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
In no particular order, some resources I took inspiration for these slides and examples:
By Kevin Cittadini