← Volver a los artículos
Arquitectura PHP y patrones de diseño

¿Qué es un DTO en PHP y por qué lo necesitas?

Descubre qué es un Data Transfer Object (DTO) en PHP, por qué es importante para la seguridad de tipos y la arquitectura limpia, y cómo implementarlo con las características modernas de PHP 8.

¿Qué es un DTO en PHP y por qué lo necesitas?
Artículo destacado ↗

¿Qué es un DTO en PHP y por qué lo necesitas?

Al construir aplicaciones PHP, los desarrolladores se enfrentan constantemente al desafío de pasar datos entre diferentes capas de una aplicación: controladores, servicios, repositorios y vistas. Un anti-patrón común es pasar arrays crudos u objetos stdClass, lo que conduce a contratos poco claros, errores ocultos y refactorización difícil. Aquí es donde entran en juego los Data Transfer Objects (DTO).

¿Qué es un DTO?

Un DTO (Data Transfer Object, objeto de transferencia de datos) es un objeto simple cuyo único propósito es transportar datos entre procesos o capas de una aplicación. No contiene lógica de negocio, reglas de validación ni consultas a la base de datos — solo propiedades y métodos para acceder a ellas.

El término fue popularizado por Martin Fowler en su libro Patterns of Enterprise Application Architecture. En el mundo PHP, los DTO se adoptaron ampliamente con el auge del Domain-Driven Design (DDD) y la arquitectura limpia.

¿Por qué necesitas los DTO?

Usar DTO en lugar de arrays u objetos genéricos ofrece varios beneficios importantes:

  • Seguridad de tipos — los DTO tienen propiedades estrictamente tipadas, lo que detecta errores temprano y mejora el autocompletado del IDE.
  • Contratos claros — un DTO define explícitamente qué datos transporta, a diferencia de un array simple con claves desconocidas.
  • Inmutabilidad — los DTO pueden hacerse de solo lectura, evitando la modificación accidental de datos mientras viajan por el sistema.
  • Encapsulación — la estructura de datos está separada de la lógica de negocio, manteniendo tu código limpio y enfocado.
  • Refactorización más fácil — cambiar la estructura de un DTO es un cambio puntual, no una cacería a través de docenas de accesos a arrays.
  • Auto-documentación — la propia clase describe la forma de los datos.

Un ejemplo simple de DTO en PHP

Creemos un DTO básico para un usuario:

<?php declare(strict_types=1); final class UserDTO { public function __construct( public readonly int $id, public readonly string $name, public readonly string $email, public readonly int $age, ) { } }

Gracias a las propiedades readonly de PHP 8.1+ y a la promoción de constructor, este DTO es conciso, inmutable y seguro en cuanto a tipos.

Usar un DTO en la práctica

Así es como podrías usar un DTO al devolver datos desde un repositorio:

<?php final class UserRepository { public function findById(int $id): ?UserDTO { $row = $this->db->fetchOne('SELECT * FROM users WHERE id = ?', [$id]); if ($row === null) { return null; } return new UserDTO( id: (int) $row['id'], name: $row['name'], email: $row['email'], age: (int) $row['age'], ); } }

Ahora el código que llama sabe exactamente qué datos recibe:

<?php $user = $repository->findById(42); if ($user !== null) { echo $user->name; // El IDE sabe que es una cadena echo $user->email; // El IDE sabe que es una cadena }

DTO vs. Value Object vs. Entity

Es importante no confundir los DTO con otros patrones:

  • DTO — transporta datos, sin comportamiento, sin identidad.
  • Value Object — inmutable, definido por sus valores, puede contener comportamiento (por ejemplo, Money, Email).
  • Entity — tiene una identidad única y un ciclo de vida, contiene lógica de negocio.

Mapear arrays a DTO

En aplicaciones reales, a menudo necesitas construir un DTO a partir de un array, como el payload de una petición. Un enfoque común es un método de fábrica estático:

<?php final class CreateUserDTO { public function __construct( public readonly string $name, public readonly string $email, public readonly int $age, ) { } public static function fromArray(array $data): self { return new self( name: $data['name'], email: $data['email'], age: (int) $data['age'], ); } }

DTO y validación

Los DTO no deben realizar la validación por sí mismos. En su lugar, mantén la validación en una capa dedicada, como un form request o un servicio validador. El DTO se crea solo después de que los datos hayan sido validados.

<?php $data = $request->getParsedBody(); $validator->validate($data, [ 'name' => 'required|string', 'email' => 'required|email', 'age' => 'required|integer|min:18', ]); $dto = CreateUserDTO::fromArray($data);

Cuándo los DTO pueden ser excesivos

Los DTO no son siempre necesarios. En scripts pequeños o prototipos, usar arrays puede ser perfectamente adecuado. Introducir DTO tiene sentido cuando:

  • Tu aplicación tiene múltiples capas con fronteras claras.
  • Los datos se pasan a través de fronteras de módulos o servicios.
  • Necesitas seguridad de tipos y mantenibilidad a largo plazo.
  • Múltiples consumidores dependen de la misma estructura de datos.

Mejores prácticas para DTO en PHP

  • Hazlos inmutables — usa propiedades readonly o propiedades privadas con getters.
  • Mantenlos pequeños — un DTO debe representar un concepto claro.
  • Sin lógica de negocio — mueve cualquier lógica a servicios u objetos de dominio.
  • Usa tipos estrictos — declara strict_types=1 en cada archivo DTO.
  • Proporciona métodos de fábrica como fromArray() o fromRequest() para una construcción cómoda.
  • Evita los setters — un DTO debe crearse una vez y nunca modificarse.

Conclusión

Los DTO son un patrón simple pero poderoso que aporta claridad, seguridad de tipos y mantenibilidad a las aplicaciones PHP. Definen contratos explícitos entre capas, reducen errores causados por arrays débilmente tipados y hacen la refactorización significativamente más fácil. Aunque añaden algo de código repetitivo, los beneficios a largo plazo en proyectos medianos y grandes superan con creces el coste. Empieza poco a poco: introduce DTO donde los datos crucen fronteras de capas, y sentirás rápidamente la diferencia en la calidad del código.

Intenta refactorizar uno de tus flujos de datos basados en arrays a un DTO hoy mismo — tu yo futuro te lo agradecerá.

Tecnologías y temas

Etiquetas del artículo

No hay artículos que coincidan con estos filtros.

¿Tienes un proyecto o una idea para discutir?

Hablemos ↗