{
  "id": 9561322,
  "title": "The Readonly Trap — PHP Value Objects & DDD Aggregates",
  "url": "https://urgent.news/2026/09/24/the-readonly-trap-php-value-objects-ddd-aggregates",
  "topic": "tech",
  "section": "Tech",
  "published": "2026-09-24T13:44:54.000Z",
  "source": {
    "name": "Dev.to",
    "slug": "dev-to",
    "url": "https://dev.to/michel_maier/the-readonly-trap-php-value-objects-ddd-aggregates-483k"
  },
  "original_language": "en",
  "account": "The Readonly Trap — PHP Value Objects & DDD Aggregates\n\nIn PHP, the readonly keyword instructs the language that a property cannot be reassigned after construction. However, this does not guarantee that two values are equal or that the contents of a Value Object remain immutable. A full booking aggregate in PHP demonstrates how readonly alone falls short in certain scenarios.\n\nA small hotel booking domain serves as an example of a proper implementation. The booking aggregate root, named Booking, contains a child Entity with its own identity and lifecycle, as well as four Value Objects responsible for the actual work. One business rule is enforced: cancelling a reservation less than 48 hours before check-in incurs a penalty charge, and only the aggregate root can handle this process.\n\nThe DateRange Value Object determines the duration of a stay and checks for overlaps with other date ranges. It ensures that check-out is after check-in and provides methods to compare with other DateRange instances. The equality method checks if both the start and end dates are identical.\n\nCurrency, an enum in PHP, defines the available currency codes, such as EUR and USD. This ensures that only valid currency codes are used within the system.\n\nThe Money Value Object represents monetary values, encapsulating cents and the associated currency. It provides methods for adding amounts, calculating percentages, and comparing Money instances. The equality comparison considers both the cents and currency properties.\n\nWhen using readonly, Value Objects maintain their immutability within the scope of the file they are defined in. However, when these objects are passed around or stored in collections, deduplication or comparison becomes a challenge. Readonly does not enforce equality or immutability in these broader contexts.\n\nTo overcome these limitations, the article suggests maintaining separate contracts for equality and immutability. While readonly ensures that a property cannot be reassigned, additional logic is needed to enforce equality and immutability when Value Objects are used in a broader system, such as in collections or during comparisons.",
  "summary": "readonly tells PHP a property can't be reassigned. It says nothing about whether two values are equal, or whether what's inside stays immutable. A full booking aggregate shows exactly where that gap bites. Every PHP DDD tutorial reaches the same beat: make your Value Object a final readonly class , validate in the constructor, done, immutability achieved. That's not wrong. It's incomplete in a…",
  "key_points": [
    "Readonly keyword prevents property reassignment after construction in PHP",
    "Value Objects remain immutable within file scope, not across broader system",
    "Additional logic needed for equality and immutability enforcement in collections"
  ],
  "editors_take": null,
  "illustration": null,
  "coverage": {
    "outlets": 1,
    "also_reported_by": []
  },
  "ai_generated": true,
  "disclaimer": "Summaries, key points and the editor’s take are written by software from other outlets’ reporting and may contain errors — always check the linked original."
}