Designing robust domain models requires strict encapsulation. Historically, implementing Domain-Driven Design (DDD) principles in PHP came at a heavy cost: endless boilerplate code, manual data validation loops, and exhaustive encapsulation setups that often drowned out the core business intent beneath layers of syntactic noise.
With the release of PHP 8.4, backend engineering has fundamentally shifted toward engine-level modernity. By leveraging advanced features such as Constructor Property Promotion, Asymmetric Visibility (public, private (set)), and Property Hooks, developers can now build elegant, highly performant, and resilient domain layers natively within the Zend Engine.
This guide provides a production-grade blueprint for building an enterprise-ready Invoice Value Object that cleanly enforces business invariants without sacrificing system performance.
In Domain-Driven Design, Value Objects are defined not by a unique identifier, but entirely by their attributes. They represent descriptive aspects of the domain (such as Money, Email, or an Invoice Summary) and are inherently immutable. When any value changes, it does not mutate the existing instance; instead, it yields a completely new instance.
Historically, achieving this structural safety required writing exhausting boilerplate code—private properties, multi-line constructor assignments, and verbose public getter methods just to make the data publicly readable but protected from external modification. PHP 8.4 eliminates this tradeoff between data integrity and verbose boilerplate, allowing us to build lightweight, self-validating, and engine-optimized structural blocks.
Below is a detailed implementation of an enterprise-grade Invoice Value Object. It combines strict execution types, constructor promotion, asymmetric visibility constraints, and virtual property hooks to manage internal calculations natively.
1<?php 2 3declare(strict_types=1); 4 5namespace App\Domain\Billing; 6 7use InvalidArgumentException; 8 9/**10 * Enterprise Invoice Value Object11 * Optimized for PHP 8.4+ Engine-Level Encapsulation12 */13final class Invoice14{15 /**16 * Virtual property computed dynamically on demand.17 */18 public float $total {19 get => $this->subtotal + $this->tax;20 }21 22 /**23 * Leverage Constructor Property Promotion and Asymmetric Visibility24 * to protect individual invariants and properties natively.25 */26 public function __construct(27 public private(set) string $invoiceNumber,28 public private(set) float $subtotal {29 set {30 if ($value <= 0) {31 throw new InvalidArgumentException("Subtotal must be greater than zero.");32 }33 $this->subtotal = $value;34 }35 },36 public private(set) float $tax {37 set {38 if ($value < 0) {39 throw new InvalidArgumentException("Tax cannot be negative.");40 }41 $this->tax = $value;42 }43 },44 public private(set) string $currency = 'USD'45 ) {}46 47 /**48 * Business Logic: Returns a completely new instance to preserve immutability.49 */50 public function withAdjustedTax(float $newTax): self51 {52 return new self(53 $this->invoiceNumber,54 $this->subtotal,55 $newTax,56 $this->currency57 );58 }59}
1. Asymmetric Visibility (public private(set))
The combination of public readability and restricted write access allows properties to be safely exposed. External layers can query data directly ($invoice->subtotal) without routing execution through userland PHP getter methods, while mutations are prevented outside of the object's explicit boundaries. This natively establishes compile-time and runtime data immutability.
2. Property Hooks (get and set)
Property Hooks allow us to attach operations directly to the property declaration.
The set Hook: Operates on input validation and manipulation automatically when data is assigned during instantiation. The $value variable represents the incoming data payload.
The get Hook: Used here to build a virtual property ($total). The property $total does not exist as a standard state property in memory ; it is computed dynamically on demand when accessed.
3. Shorthand vs. Block Syntax
PHP 8.4 supports both shorthand arrow functions (=>) for immediate, straightforward transformations or retrievals, and full block syntax ({ ... }) for complex logic blocks requiring validation rules and explicit assignment execution paths.
Shifting data protection and access rules from userland PHP code directly into the engine core provides substantial engineering advantages for modern backend applications:
Massive Memory & Performance Optimizations: By routing data access patterns directly through the engine core rather than manual userland PHP getter/setter methods, execution pathways are streamlined and instruction overhead is minimized.
Elimination of Static Noise: Codebases become strictly expressive. Domain entities and value objects focus directly on representing business rules and core domain logic rather than working around language syntax limitations.
Seamless Tooling Integration: Modern static analysis engines like PHPStan and Psalm parse asymmetric visibility definitions and property hooks natively. This guarantees ironclad code predictability and type safety within isolated development loops or GitHub Actions CI/CD pipelines before production deployments.
PHP.net: PHP 8.4 Release Announcement
PHP Internals RFC: PHP RFC: Property Hooks
PHP Internals RFC: PHP RFC: Asymmetric Visibility
Zend Blog: A Guide to PHP 8.4 Property Hooks
Static Analysis Industry Standards: PHPStan Documentation & Psalm Main Documentation
About the author
Darren Odden is a seasoned software developer and web architect specializing in the modern PHP and Laravel ecosystems, where he designs elegant APIs and robust web applications. As a dedicated tech advocate, he focuses on community building and championing clean, modern development practices. When he isn’t diving into code or fine-tuning tech stacks, Darren balances his digital life by hitting the open road with his family in their travel trailer, camping, solving crossword puzzles, and immersing himself in the rich subcultures of classic hip-hop and vintage graffiti art.
Built for Developers, by Developers
Join the movement and discover why modern PHP is the sophisticated choice for elegant, high-scale applications in 2026.
Reach Us
Santa Cruz, CA 95062