Immutable value objects for domain values. Use when working with domain values, immutable objects, or when user mentions value objects, immutable values, domain values, money objects, coordinate objects.
Value objects are simple, immutable objects representing domain concepts.
Related guides:
Use value objects when:
Use DTOs when:
<?php
declare(strict_types=1);
namespace App\Values;
use App\Enums\ProcessResult as ProcessResultEnum;
class ProcessResult
{
public function __construct(
public readonly ProcessResultEnum $result,
public readonly ?string $message = null,
) {}
public static function success(?string $message = null): self
{
return new self(ProcessResultEnum::Success, $message);
}
public static function skip(?string $message = null): self
{
return new self(ProcessResultEnum::Skip, $message);
}
public static function fail(?string $message = null): self
{
return new self(ProcessResultEnum::Fail, $message);
}
public function isSuccess(): bool
{
return $this->result === ProcessResultEnum::Success;
}
public function isFail(): bool
{
return $this->result === ProcessResultEnum::Fail;
}
}
// In actions
return ProcessResult::success('Order processed successfully');
return ProcessResult::skip('Order already processed');
return ProcessResult::fail('Payment declined');
// Checking results
if ($result->isSuccess()) {
// Handle success
}
if ($result->isFail()) {
// Handle failure
}
// Creating money values
$price = Money::fromDollars(29.99);
$tax = Money::fromDollars(2.40);
$shipping = Money::fromCents(500); // $5.00
// Operations
$subtotal = $price->add($tax);
$total = $subtotal->add($shipping);
// Multiplication
$bulkPrice = $price->multiply(10);
// Display
echo $total->formatted(); // "37.39"
// Comparison
if ($total->equals($expectedTotal)) {
// Amounts match
}
Use readonly properties:
public readonly int $amount;
public readonly string $currency;
Named constructors for common scenarios:
public static function fromDollars(float $dollars): self
public static function success(?string $message = null): self
Force use of factory methods:
private function __construct(/* ... */) {}
Encapsulate domain rules:
public function add(Money $other): self
{
$this->assertSameCurrency($other);
return new self($this->amount + $other->amount, $this->currency);
}
Operations return new instances (immutability):
public function add(Money $other): self
{
return new self($this->amount + $other->amount, $this->currency);
}
app/Values/
├── Money.php
├── ProcessResult.php
├── Coordinate.php
└── EmailAddress.php
Value objects:
readonly)Use for domain concepts with behavior, not simple data transfer.