jkisolo.com

Understanding the Value Object Pattern in Domain-Driven Design

Written on

Introduction to Value Object Pattern

The Value Object pattern is among the simplest design patterns to grasp and apply. Essentially, it defines an object that encapsulates a specific value. Notably, these objects are immutable, meaning their state cannot be altered after creation. Two Value Objects are considered equal if their respective properties match. While this pattern is part of Domain-Driven Design (DDD), it can be effectively utilized in various applications, even outside the DDD framework. Implementing this pattern can significantly enhance code readability, comprehension, and maintainability while minimizing potential errors.

This article will guide you through the Value Object pattern and demonstrate its implementation in PHP. Though straightforward, this concept can be immensely beneficial for your projects.

Example 1 — Money

Consider a Money class designed to represent monetary values. It comprises two properties: amount and currency. You can instantiate a Money object by passing these parameters to its constructor. Maintaining these properties together is crucial due to their intrinsic relationship.

class Money

{

private int $amount;

private Currency $currency;

public function __construct(int $amount, Currency $currency)

{

if ($amount < 0) {

throw new DomainException('Amount cannot be negative');

}

$this->amount = $amount;

$this->currency = $currency;

}

public function add(Money $money): Money

{

if ($money->currency !== $this->currency) {

throw new DomainException('Cannot add money with different currency');

}

return new Money($this->amount + $money->amount, $this->currency);

}

public function subtract(Money $money): Money

{

if ($money->currency !== $this->currency) {

throw new DomainException('Cannot subtract money with different currency');

}

if ($money->amount > $this->amount) {

throw new DomainException('Cannot subtract more than available');

}

return new Money($this->amount - $money->amount, $this->currency);

}

public function equals(Money $money): bool

{

return $this->amount === $money->amount && $this->currency === $money->currency;

}

}

As illustrated, the Money class is a straightforward example of a value object that safeguards both the amount and currency from modification. It includes methods for adding, subtracting, and comparing monetary values, while also guarding against invalid operations, such as combining different currencies or deducting more than the available amount.

In the accompanying video, "Value Object Design Pattern Explained," the nuances of this pattern are discussed in detail, providing further insights into its significance and application.

The Currency as an Enum

You can also create additional value objects, such as Email, PhoneNumber, and Address. Furthermore, collections of value objects can be developed, such as MoneyCollection or EmailCollection.

Testing the Money Class

Testing is critical to ensure that the Money class behaves as expected. Below are some tests for the Money class:

public function testCreateMoneyWithNegativeAmount(): void

{

$this->expectException(DomainException::class);

$this->expectExceptionMessage('Amount cannot be negative');

new Money(-100, Currency::EUR);

}

// Additional tests omitted for brevity...

In these tests, we verify the functionality of the Money class, including the ability to create a Money object, add and subtract amounts, compare two Money objects, and convert a Money object to its string representation.

Example 2 — Email

Now, let's look at an Email class that represents an email address with a single property: value. The creation of an Email object includes validation to ensure that the email address is formatted correctly and within an acceptable length.

class Email

{

private string $value;

const LENGTH_MAX = 64;

public function __construct(string $value)

{

if (strlen($value) > self::LENGTH_MAX) {

throw new InvalidArgumentException('Email address is too long');

}

if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {

throw new InvalidArgumentException('Invalid email address');

}

$this->value = $value;

}

public function __toString(): string

{

return $this->value;

}

}

Testing the Email Class

We also conduct tests for the Email class to confirm its proper functionality:

public function testInvalidEmail(): void

{

$this->expectException(InvalidArgumentException::class);

$this->expectExceptionMessage('Invalid email address');

new Email('invalid');

}

// Additional tests omitted for brevity...

Conclusion

The Value Object pattern is a powerful tool for enhancing code clarity, comprehension, and maintainability. It embodies the DRY (Don't Repeat Yourself) principle and, despite its simplicity, it holds significant value. Consider introducing this pattern to your team and implementing it in your projects; your colleagues will surely appreciate it.

If you found this article helpful, feel free to clap, follow, or leave a comment. Thank you for reading!

Share the page:

Twitter Facebook Reddit LinkIn

-----------------------

Recent Post:

Elevate Your Obsidian Experience with Top New Plugins

Explore the latest Obsidian plugins that enhance note-taking and productivity, featuring tools for every user.

Unveiling the True Size of the Vredefort Crater

New research reveals the Vredefort Crater is much larger than previously believed, shedding light on planetary defense.

Apple iPhone 14 Pro Review: My First Month Insights

After a month with the iPhone 14 Pro, here's my take on its features, performance, and whether the upgrade was worth it.