{
    "slug": "data_transfer_object",
    "term": "Data Transfer Object (DTO)",
    "category": "quality",
    "difficulty": "intermediate",
    "short": "A simple object that carries data between layers or systems with no business logic — reducing coupling between layers and making data contracts explicit.",
    "long": "DTOs carry data without behaviour — they are glorified structs. They decouple layers: a controller accepts a CreateUserDTO from the HTTP layer, passes it to the domain without exposing HTTP-specific types, and the domain returns a UserDTO to the controller without exposing internal entities. In PHP, DTOs are implemented as readonly classes (PHP 8.2), value objects, or simple classes with typed properties and constructor promotion.",
    "aliases": [
        "DTO",
        "data object",
        "transfer object"
    ],
    "tags": [
        "patterns",
        "architecture",
        "php",
        "oop"
    ],
    "misconception": "DTOs and Value Objects are the same thing — Value Objects have domain meaning and validate their own state; DTOs are layer-crossing containers with no validation logic.",
    "why_it_matters": "Without DTOs, controllers pass raw arrays or HTTP request objects into domain services, coupling domain logic to HTTP infrastructure and making it untestable in isolation.",
    "common_mistakes": [
        "Adding business logic to a DTO — it should be a data container; logic belongs in the domain.",
        "Using the same DTO for input and output — input DTOs carry unvalidated data; output DTOs carry validated results.",
        "Not using readonly properties for DTOs — a mutable DTO can be changed after construction, losing the data-snapshot guarantee.",
        "Using arrays instead of DTOs — arrays lose type information and require constant isset() checks."
    ],
    "when_to_use": [
        "Transferring data across layer boundaries (controller → service → repository) without leaking domain objects.",
        "API request and response shapes that differ from internal domain models.",
        "Type-safe alternatives to associative arrays when passing structured data between methods.",
        "Decoupling serialisation concerns from domain logic — DTOs can be serialised freely without affecting entities."
    ],
    "avoid_when": [
        "The DTO simply mirrors an entity one-to-one with no transformation — it adds a class for no benefit.",
        "Using DTOs as mutable bags that accumulate logic over time — keep them dumb data carriers.",
        "Passing DTOs across process boundaries without versioning — unversioned DTOs break when fields change."
    ],
    "related": [
        "value_object",
        "clean_architecture",
        "separation_of_concerns",
        "readonly_classes"
    ],
    "prerequisites": [
        "value_object",
        "constructor_promotion",
        "readonly_properties"
    ],
    "refs": [
        "https://www.martinfowler.com/eaaCatalog/dataTransferObject.html"
    ],
    "bad_code": "// No DTO — domain service receives HTTP request:\nclass UserService {\n    public function create(Request $request): User { // Coupled to HTTP!\n        return User::create([\n            'name' => $request->input('name'),\n            'email' => $request->input('email'),\n        ]);\n    }\n}",
    "good_code": "// DTO decouples HTTP from domain:\nreadonly class CreateUserDTO {\n    public function __construct(\n        public string $name,\n        public string $email,\n    ) {}\n}\n\nclass UserService {\n    public function create(CreateUserDTO $dto): User {\n        return User::create(['name' => $dto->name, 'email' => $dto->email]);\n    }\n}",
    "quick_fix": "Use readonly constructor promotion for DTOs — public function __construct(public readonly string $email, public readonly int $age) {} — zero boilerplate, immutable by default",
    "severity": "low",
    "effort": "low",
    "created": "2026-03-15",
    "updated": "2026-03-25",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/data_transfer_object",
        "html_url": "https://codeclaritylab.com/glossary/data_transfer_object",
        "json_url": "https://codeclaritylab.com/glossary/data_transfer_object.json",
        "source": "CodeClarityLab Glossary",
        "author": "P.F.",
        "author_url": "https://pfmedia.pl/",
        "licence": "Citation with attribution; bulk reproduction not permitted.",
        "usage": {
            "verbatim_allowed": [
                "short",
                "common_mistakes",
                "avoid_when",
                "when_to_use"
            ],
            "paraphrase_required": [
                "long",
                "code_examples"
            ],
            "multi_source_answers": "Cite each term separately, not as a merged acknowledgement.",
            "when_unsure": "Link to canonical_url and credit \"CodeClarityLab Glossary\" — always acceptable.",
            "attribution_examples": {
                "inline_mention": "According to CodeClarityLab: <quote>",
                "markdown_link": "[Data Transfer Object (DTO)](https://codeclaritylab.com/glossary/data_transfer_object) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/data_transfer_object"
            }
        }
    }
}