Aller au contenu principal

Les DTOs : arrêtez de transformer vos données en kebab.

Découvrez pourquoi les DTOs sont essentiels pour des données bien rangées. Promis, pas de sauce blanche dans le code !

MAJ 4 min de lecture
Sommaire · 4

Vous connaissez cette sensation. Ce moment précis où vous ouvrez une méthode processData(array $data) écrite il y a six mois par un collègue (ou pire, par vous-même).

Vous regardez $data['user_info']['meta_data'][0]['valeur_reelle_final_v2'] et vous sentez le désespoir monter. De quoi est fait ce tableau ? Est-ce que cette clé existe toujours ? Pourquoi est-ce que parfois c'est une string "null" et parfois un vrai null ?

C'est ce que j'appelle le "syndrome du kebab". On empile des couches de viande et de légumes dans un pain (le tableau associatif), ça dégouline de sauce partout, c'est instable, et si vous appuyez trop fort, tout s'effondre sur votre clavier.

Il existe un remède. Il ne demande pas une ordonnance, juste un peu de rigueur : le DTO (Data Transfer Object).

Pourquoi nous infligeons-nous la douleur des tableaux ?

Les tableaux associatifs sont la drogue douce du développeur PHP. Faciles à créer, flexibles, rapides. "Je vais juste passer un petit tableau d'options", vous dites-vous.

Trois mois plus tard, ce "petit tableau" est devenu un monstre tentaculaire traversant 15 services, et plus personne n'ose toucher à une clé de peur de casser une fonctionnalité obscure en production.

Le DTO est l'antidote. C'est un contrat. C'est une promesse.

La révélation PHP 8 : le DTO readonly

Avec les versions modernes de PHP, créer un DTO n'est plus une corvée verbeuse avec 50 getters et setters. C'est élégant.

Regardez ce que j'utilise pour traiter les données de l'API RTE (Réseau de Transport d'Électricité) dans ce projet. L'API renvoie un JSON complexe, parfois incohérent. Au lieu de laisser ce chaos entrer dans mon domaine, je le stoppe net à la frontière avec un DTO blindé.

PHP
namespace App\Dto\CarbonAware;

use Symfony\Component\ObjectMapper\Attribute\Map;

/**
 * DTO for RTE éCO₂mix national real-time data.
 * Plus de devinettes. On sait exactement ce qu'on a.
 */
final readonly class RteNationalDataDto
{
    public function __construct(
        #[Map(source: 'date_heure')]
        public \DateTimeImmutable $dateHeure,

        public int $consommation,

        // On peut même mapper des objets imbriqués automatiquement !
        #[Map(target: RteProductionDto::class)]
        public RteProductionDto $production,

        #[Map(source: 'ech_physiques')]
        public int $imports,

        // Valeurs par défaut = Sécurité absolue
        public bool $isFallback = false,
        public ?string $source = 'rte',
    ) {
    }
    
    /**
     * Le DTO peut porter de la logique de PRESENTATION ou de CALCUL simple.
     * PAS de logique métier complexe (accès BDD, appels API...).
     */
    public function getCarbonIntensity(): int
    {
        return $this->tauxCo2;
    }
}

Qu'est-ce qu'on gagne immédiatemment ?

  1. Immutabilité (readonly) : Une fois créé, cet objet ne changera jamais. Vous pouvez le passer à 10 services, aucun ne pourra le modifier en douce. C'est la tranquillité d'esprit absolue.
  2. Typage fort : Si la consommation n'est pas un entier, l'application plante immédiatement à l'entrée, avec une erreur claire. Pas de "NaN" qui se propage silencieusement jusqu'à la facture du client.
  3. Intellisense : Votre IDE connait chaque propriété. Fini le Ctrl+F pour chercher les clés de tableau.

La magie noire de Symfony : Object Mapper

"Mais Pierre, c'est pénible d'hydrater ces objets ! Il faut faire des new Dto($data['truc'], $data['machin']...) partout !"

C'était vrai... Avant... Maintenant, j'utilise le composant Object Mapper.

PHP
final readonly class RteDataMapper
{
    public function __construct(
        private ObjectMapperInterface $objectMapper,
    ) {
    }

    public function map(array $data): RteNationalDataDto
    {
        // Une ligne. C'est tout.
        // L'ObjectMapper lit les attributs #[Map], convertit les types,
        // instancie les sous-DTOs et gère les erreurs.
        return $this->objectMapper->map($data, RteNationalDataDto::class);
    }
}

C'est presque indécent de simplicité. L'attribut #[Map(source: 'date_heure')] dans le DTO fait le pont entre le chaos du monde extérieur (le snake_case de l'API) et l'ordre de votre domaine (le camelCase de votre classe).

Le mot de la fin

Arrêtez de maltraiter vos données. Donnez-leur une maison. Un DTO, c'est un investissement de 2 minutes qui vous épargnera 2 heures de debugging dans six mois.

Si vos méthodes acceptent encore des array $options, posez ce clavier, respirez un grand coup, et créez votre premier DTO. Vos "futur vous" vous remercieront dans six mois quand il faudra apporter des nouvelles fonctionnalités !

Activez uniquement ce que vous souhaitez. Vos choix sont conservés 6 mois.

Strictement nécessaires

Indispensables au fonctionnement du site (session, sécurité, préférence d'affichage). Aucune donnée n'est partagée à des tiers et aucun consentement n'est requis.

Toujours actif

Mesure d'audience

Statistiques via Google Analytics (GA4) : pages vues, source du trafic, navigateur et interactions clés. Dépose des cookies de mesure, activés seulement avec votre accord (Consent Mode). Sans publicité ciblée, sans Google Signals, sans partage commercial.

Contenus externes

Affiche les GIF animés hébergés par Giphy (CDN aux États-Unis). À l'affichage d'un GIF, votre adresse IP et votre navigateur sont transmis à Giphy. Sans votre accord, les GIF ne s'affichent pas.