← Retour aux articles
Architecture PHP et patrons de conception

Qu'est-ce qu'un DTO en PHP et pourquoi en avez-vous besoin ?

Découvrez ce qu'est un Data Transfer Object (DTO) en PHP, pourquoi il est important pour la sécurité des types et l'architecture propre, et comment l'implémenter avec les fonctionnalités modernes de PHP 8.

Qu'est-ce qu'un DTO en PHP et pourquoi en avez-vous besoin ?
Article en vedette ↗

Qu'est-ce qu'un DTO en PHP et pourquoi en avez-vous besoin ?

Lors de la création d'applications PHP, les développeurs sont constamment confrontés au défi de faire passer des données entre différentes couches d'une application : contrôleurs, services, dépôts et vues. Un anti-pattern courant consiste à passer des tableaux bruts ou des objets stdClass, ce qui conduit à des contrats peu clairs, des bugs cachés et un refactoring difficile. C'est là que les Data Transfer Objects (DTO) viennent à la rescousse.

Qu'est-ce qu'un DTO ?

Un DTO (Data Transfer Object, objet de transfert de données) est un objet simple dont l'unique but est de transporter des données entre des processus ou des couches d'une application. Il ne contient aucune logique métier, aucune règle de validation et aucune requête de base de données — uniquement des propriétés et des méthodes pour y accéder.

Le terme a été popularisé par Martin Fowler dans son livre Patterns of Enterprise Application Architecture. Dans le monde PHP, les DTO sont devenus largement adoptés avec l'essor du Domain-Driven Design (DDD) et de l'architecture propre.

Pourquoi avez-vous besoin des DTO ?

L'utilisation de DTO au lieu de tableaux ou d'objets génériques offre plusieurs avantages importants :

  • Sécurité des types — les DTO ont des propriétés strictement typées, ce qui détecte les erreurs tôt et améliore l'autocomplétion de l'IDE.
  • Contrats clairs — un DTO définit explicitement les données qu'il transporte, contrairement à un tableau simple avec des clés inconnues.
  • Immutabilité — les DTO peuvent être rendus en lecture seule, empêchant la modification accidentelle des données lors de leur parcours dans le système.
  • Encapsulation — la structure des données est séparée de la logique métier, gardant votre code propre et ciblé.
  • Refactoring plus facile — modifier la structure d'un DTO est un changement ponctuel, et non une chasse à travers des dizaines d'accès à des tableaux.
  • Auto-documentation — la classe elle-même décrit la forme des données.

Un exemple simple de DTO en PHP

Créons un DTO de base pour un utilisateur :

<?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, ) { } }

Grâce aux propriétés readonly de PHP 8.1+ et à la promotion de constructeur, ce DTO est concis, immuable et sûr au niveau des types.

Utilisation d'un DTO en pratique

Voici comment vous pourriez utiliser un DTO lors du retour de données depuis un dépôt :

<?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'], ); } }

Maintenant, le code appelant sait exactement quelles données il reçoit :

<?php $user = $repository->findById(42); if ($user !== null) { echo $user->name; // L'IDE sait qu'il s'agit d'une chaîne echo $user->email; // L'IDE sait qu'il s'agit d'une chaîne }

DTO vs. Value Object vs. Entity

Il est important de ne pas confondre les DTO avec d'autres patterns :

  • DTO — transporte des données, aucun comportement, aucune identité.
  • Value Object — immuable, défini par ses valeurs, peut contenir un comportement (par exemple, Money, Email).
  • Entity — possède une identité unique et un cycle de vie, contient la logique métier.

Mapper des tableaux vers des DTO

Dans les applications réelles, vous devez souvent construire un DTO à partir d'un tableau, comme une charge utile de requête. Une approche courante est une méthode de fabrique statique :

<?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 et validation

Les DTO ne doivent pas effectuer eux-mêmes la validation. Au lieu de cela, gardez la validation dans une couche dédiée, comme une form request ou un service de validation. Le DTO n'est créé qu'après que les données ont été validées.

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

Quand les DTO peuvent être superflus

Les DTO ne sont pas toujours nécessaires. Dans les petits scripts ou prototypes, l'utilisation de tableaux peut être parfaitement appropriée. L'introduction de DTO a du sens lorsque :

  • Votre application comporte plusieurs couches avec des frontières claires.
  • Les données sont transmises à travers les frontières de modules ou de services.
  • Vous avez besoin de sécurité des types et de maintenabilité à long terme.
  • Plusieurs consommateurs s'appuient sur la même structure de données.

Meilleures pratiques pour les DTO en PHP

  • Rendez-les immuables — utilisez des propriétés readonly ou des propriétés privées avec des getters.
  • Gardez-les petits — un DTO doit représenter un concept clair.
  • Aucune logique métier — déplacez toute logique vers des services ou des objets de domaine.
  • Utilisez des types stricts — déclarez strict_types=1 dans chaque fichier DTO.
  • Fournissez des méthodes de fabrique comme fromArray() ou fromRequest() pour une construction pratique.
  • Évitez les setters — un DTO doit être créé une fois et jamais modifié.

Conclusion

Les DTO sont un pattern simple mais puissant qui apporte clarté, sécurité des types et maintenabilité aux applications PHP. Ils définissent des contrats explicites entre les couches, réduisent les bugs causés par des tableaux faiblement typés et rendent le refactoring nettement plus facile. Bien qu'ils ajoutent un peu de code boilerplate, les bénéfices à long terme dans les projets de taille moyenne et grande dépassent largement le coût. Commencez petit : introduisez des DTO là où les données franchissent les frontières des couches, et vous ressentirez rapidement la différence en qualité de code.

Essayez de refactoriser l'un de vos flux de données basés sur des tableaux en un DTO dès aujourd'hui — votre futur vous remerciera.

Technologies et sujets

Tags de l'article

Aucun article ne correspond à ces filtres.

Vous avez un projet ou une idée à discuter ?

Parlons-en ↗