"""The :class:`UserAddress` value object. A frozen dataclass capturing the splitter's domain entity: the raw input address line, a sanitised postcode, and an optional internal reference from the customer dataset. Postcode sanitisation runs in ``__post_init__`` so no caller can construct an instance with an un-normalised postcode. """ from __future__ import annotations from dataclasses import dataclass from typing import Optional from domain.postcodes.sanitise import sanitise_postcode @dataclass(frozen=True) class UserAddress: """A user-supplied address paired with its canonical postcode. Attributes: user_address: The free-text address string as supplied upstream. postcode: The postcode; always stored in canonical form (uppercased, whitespace stripped). Sanitisation is enforced by :meth:`__post_init__`. internal_reference: Optional customer-side identifier preserved for traceability through the matching pipeline. """ user_address: str postcode: str internal_reference: Optional[str] = None def __post_init__(self) -> None: # Frozen dataclass: bypass the descriptor with object.__setattr__. object.__setattr__(self, "postcode", sanitise_postcode(self.postcode))