readonly classes: practical patterns beyond simple DTOs
readonly class was the headline feature of PHP 8.2. Most examples show trivial DTOs. What are more interesting use cases?
Value objects in domain models. A Money class with amount and currency that you never want mutated. readonly enforces immutability without a private constructor pattern.
Configuration objects passed through the system. Instead of an array or stdClass that can be modified anywhere, a readonly config object with typed properties catches bugs at the mutation point.
Command and query objects in CQRS. They represent intent, not mutable state. readonly guarantees the command cannot be altered after dispatch.
One limitation: readonly classes cannot have non-initialized properties or properties with default values that get set later. For optional properties you still need nullable types with null defaults initialized in the constructor.
Serialization and deserialization is a practical problem with readonly: you cannot unserialize into a readonly class without a custom unserialize handler. For cached objects this is a real constraint.
Cloning readonly objects with a mutation via clone $obj with {property: newValue} syntax is coming in future versions. For now you need a wither method that returns a new instance.
```php blocks are runnable.