DDD avec Symfony : Comment configurer le mapping XML de Doctrine
La configuration XML qui a l'avantage de rester dans la couche infrastructure ce qui fait que notre entité ignore l'ORM utilisé et nous permet de garantir le respect des exigences architecturales.

Doctrine est un ORM pour PHP qui fournit une persistance transparente pour les objets PHP. Il utilise le modèle Data Mapper, qui vise à séparer complètement votre domaine/logique commerciale de la persistance dans un système de gestion de base de données relationnelle.
Dans une application Symfony classique, la configuration des entités Doctrine se fait par le biais d'annotations (ou d'attributs avec PHP +8.0).
Dans cet article nous allons voir
- Pourquoi il est préférable d'utiliser le mapping XML quand on fait du DDD
- Comment configurer le mapping XML de Doctrine
Pourquoi il est préférable d'utiliser le mapping XML quand on fait du DDD
Commençons par un exemple simple, dans une application Symfony classique voici à quoi ressemblerait une entité
<?php
declare(strict_types=1);
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Table(name: '`user`')]
#[ORM\Entity(repositoryClass: UserRepository::class)]
class User
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private ?int $id = null;
#[ORM\Column(type: 'string', length: 180, unique: true)]
private ?string $email = null;
#[ORM\Column(type: 'json')]
private array $roles = ['ROLE_USER'];
#[ORM\Column(type: 'string')]
private ?string $password = null;
// ...
}
Bien sûr n'oublions pas les accesseurs et mutateurs pour chaque propriété puisqu'ils sont privés, pour des raisons de concision je ne les mettrai pas dans mes exemples.
Une chose que l'on peut remarquer très rapidement en regardant notre classe utilisateur qui représente évidemment un utilisateur dans le contexte de notre domaine a des méta données provenant de Doctrine, ce qui peut être gênant car l'idéal serait d'avoir le code du domaine ignorant d'une certaine manière l'infrastructure, rappelez-vous que ce n'est pas une obligation dans notre cas comme Matthias Noback l'explique dans cet article, car ce ne sont que des méta données et non un couplage donc l'entité reste testable de manière isolée et ne dépend pas de Doctrine.
Une précision importante pour les puristes, je ne compare pas une entité DDD à une entité Doctrine, les deux concepts sont différents, mon but ici est de montrer comment on peut se débarrasser des annotations (ou attributs).
Ceci dit, l'utilisation de XML a l'avantage de retirer du code de votre domaine toutes les méta-données de Doctrine et de les déplacer dans une configuration dédiée qui sera évidemment dans l'infrastructure, si vous utilisez des outils comme phpat pour garantir le respect de certaines exigences architecturales cette approche sera préférable.
Les concepteurs de Doctrine ont pensé à ce cas d'utilisation très particulier et ont développé d'autres moyens de mapper en dehors des annotations (ou attributs), nous pouvons utiliser PHP, YAML ou XML, malheureusement le support de YAML et PHP sera supprimé à partir de la version 3.0 et il est recommandé d'utiliser XML ce que nous allons faire dès maintenant.
Comment configurer Doctrine Mapping avec XML
L'exemple que j'ai donné précédemment est valable dans une application Symfony classique mais ici nous faisons du DDD et notre architecture devrait plutôt ressembler à ceci
src/
├── Application/
├── Domain/
│ └── Authentication/
│ ├── Entity/
│ │ ├── User.php
│ │ └── ...
│ └── Repository/
│ └── UserRepository.php (interface)
└── Infrastructure/
└── Authentication/
└── Doctrine/
├── Mapping/
│ └── User.orm.xml
├── Repository/
│ └── UserRepository (implementation)
└── ...
Ok, il y a beaucoup d'informations ici, tout d'abord remarquez que nous avons deux fois le UserRepository
, celui qui est dans le domaine est une simple interface et son implémentation (classe concrète) est dans l'infrastructure ceci pour éviter de dépendre de la doctrine de notre Domain Repository.
Pour que votre mapping XML soit pris en compte par doctrine avant de configurer quoi que ce soit, le nom du fichier doit correspondre à ce pattern [EntityName].orm.xml
, l'extension du fichier .orm.xml
est importante
Avec cette façon de faire, notre utilisateur d'entité devrait maintenant ressembler à ceci
<?php
declare(strict_types=1);
namespace App\Entity;
use App\Repository\UserRepository;
class User
{
private ?int $id = null;
private ?string $email = null;
private array $roles = ['ROLE_USER'];
private ?string $password = null;
// ...
}
Il n'y a plus d'annotations (ou d'attributs) provenant de la doctrine dans notre domaine, l'étape suivante consiste à transcrire en XML le mapping précédemment écrit avec les annotations (ou attributs) dans notre Infrastructure.
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Domain\Authentication\Entity\User" repository-class="Infrastructure\Authentication\Doctrine\Repository\UserRepository" table="user">
<id name="id" type="integer" column="id">
<generator strategy="IDENTITY"/>
</id>
<field name="name" type="string"/>
<field name="email" type="string" length="180" unique="true"/>
<field name="roles" type="json"/>
<field name="password" type="string"/>
</doctrine-mapping>
Notez que nous spécifions le FQCN de notre entité et le repository (implémentation) associé directement dans le mapping, vous pouvez trouver plus d'informations ici.
Nous sommes presque arrivés, il reste une dernière étape, nous devons maintenant "dire" à Doctrine comment trouver notre mapping et où le trouver et cela se fait par la configuration de doctrine située dans `/config/packages/doctrine.yaml``
orm:
mappings:
Domain\Authentication\Entity:
type: xml
dir: '%kernel.project_dir%/src/Infrastructure/Authentication/Doctrine/Mapping'
prefix: 'Domain\Authentication\Entity'
alias: Authentication
is_bundle: false
Pour vérifier que tout fonctionne bien, il suffit de taper la commande suivante : php bin/console doctrine:mapping:info
.
Conclusion
Pour finir, faisons un petit récapitulatif, quand on fait du DDD dans le cas d'une application Symfony pour les entités on utilise la configuration XML qui a l'avantage de rester dans la couche infrastructure ce qui fait que notre entité ignore l'ORM utilisé et nous permet de garantir le respect des exigences architecturales.