Domain-Driven Design in PHP
上QQ阅读APP看书,第一时间看更新

Currency and Money Example

The Currency and Money Value Objects are probably the most used examples for explaining Value Objects, thanks to the Money pattern. This design pattern provides a solution for modeling a problem that avoids a floating-point rounding issue, which in turn allows for deterministic calculations to be performed.

In the real world, a currency describes monetary units in the same way that meters and yards describe distance units. Each currency is represented with a three-letter uppercase ISO code:

class Currency 
{
private $isoCode;


public function __construct($anIsoCode)
{
$this->setIsoCode($anIsoCode);
}

private function setIsoCode($anIsoCode)
{
if (!preg_match('/^[A-Z]{3}$/', $anIsoCode)) {
throw new InvalidArgumentException();
}

$this->isoCode = $anIsoCode;
}

public function isoCode()
{
return $this->isoCode;
}
}

One of the main goals of Value Objects is also the holy grail of Object-Oriented design: encapsulation. By following this pattern, you'll end up with a dedicated location to put all the validation, comparison logic, and behavior for a given concept.

Extra Validations for Currency
In the previous code example, we can build a Currency with an AAA Currency ISO code. That isn't valid at all. Write a more specific rule that will check if the ISO Code is valid. A full list of valid currency ISO codes can be found here. If you need help, take a look at the Money packagist library.

Money is used to measure a specific amount of currency. It's modeled using an amount and a currency. Amount, in the case of the Money pattern, is implemented using an integer representation of the Currency's least-valuable fraction — For example   in the case of USD or EUR, cents.

As a bonus, you might also notice that we're using self encapsulation to set the ISO code, which centralizes changes in the Value Object itself:

class Money 
{
private $
amount; 
private $
currency;

public function __construct($anAmount, Currency $aCurrency)
{
$this->setAmount($anAmount);
$this->setCurrency($aCurrency);
}

private function setAmount($anAmount)
{
$this->amount = (int) $anAmount;
}

private function setCurrency(Currency $aCurrency)
{
$this->currency = $aCurrency;
}

public function amount()
{
return $this->amount;
}

public function currency()
{
return $this->currency;
}
}

Now that you know the formal definition of Value Objects, let's dive deeper into some of the powerful features they offer.