Ports et adaptateurs expliqués
Isolez le cœur grâce à des ports et à des adaptateurs interchangeables.
Ports et adaptateurs expliqués est une leçon PHP Academy gratuite sur CoddyKit. Ceci est la leçon 2 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage PHP Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours PHP Academy comprend 4 leçons au total.
L’idée de l’hexagone
Ports et adaptateurs — l’architecture hexagonale d’Alistair Cockburn — représente votre application sous la forme d’un hexagone. À l’intérieur se trouve une logique métier pure. Chaque interaction avec le monde extérieur (HTTP, base de données, file d’attente, horloge, courriel) franchit un port, et chaque port est pris en charge par un ou plusieurs adaptateurs. Cette forme ne privilégie ni le haut ni le bas : l’interface utilisateur et la base de données sont symétriques, toutes deux n’étant que des adaptateurs.
Les ports sont des interfaces
Un port est une interface appartenant au cœur applicatif, qui exprime un besoin ou une capacité en termes de domaine. Il ne doit pas laisser transparaître le vocabulaire de l’infrastructure — ni PDOStatement, ni GuzzleResponse, ni Eloquent.
<?php
// Driven (outbound) port: the core needs to persist users
interface UserRepository
{
public function byId(UserId $id): ?User;
public function save(User $user): void;
}Ports de pilotage et ports pilotés
Il en existe deux types :
- Ports de pilotage (primaires, entrants) — l’interface que le monde extérieur appelle pour piloter l’application. Il s’agit généralement de vos interfaces de cas d’utilisation.
- Ports pilotés (secondaires, sortants) — les interfaces que l’application appelle pour atteindre le monde extérieur : référentiels, systèmes d’envoi de courriels, horloges.
Les adaptateurs de pilotage appellent le cœur ; le cœur passe par des adaptateurs pilotés pour communiquer avec l’extérieur.
<?php
// Driving (inbound) port — the public capability of the core
interface RegisterUser
{
public function handle(string $email, string $plainPassword): UserId;
}Le cœur implémente les ports de pilotage
Le cas d’utilisation implémente un port de pilotage et dépend de ports pilotés. Remarque : il accepte PasswordHasher et Clock comme ports injectés — même le temps et le hachage sont abstraits afin que le cœur reste déterministe et testable.
<?php
final class RegisterUserService implements RegisterUser
{
public function __construct(
private UserRepository $users,
private PasswordHasher $hasher,
private Clock $clock,
) {}
public function handle(string $email, string $plain): UserId {
if ($this->users->byEmail($email)) {
throw new EmailAlreadyTaken($email);
}
$user = User::register(
$email,
$this->hasher->hash($plain),
$this->clock->now()
);
$this->users->save($user);
return $user->id();
}
}Un adaptateur piloté
Un adaptateur piloté implémente un port piloté au moyen d’une technologie concrète. Ici, un adaptateur PDO satisfait UserRepository. Remplacez-le par Doctrine, Redis ou un client d’interface de programmation HTTP sans toucher au cœur.
<?php
final class PdoUserRepository implements UserRepository
{
public function __construct(private PDO $pdo) {}
public function byId(UserId $id): ?User {
$stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([(string) $id]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
return $row ? User::fromRow($row) : null;
}
public function save(User $user): void {
// INSERT ... ON CONFLICT UPDATE
}
}Un adaptateur de pilotage
Un adaptateur de pilotage traduit un déclencheur externe en appel d’un port de pilotage. Un contrôleur HTTP, une commande CLI ou un consommateur de messages sont tous des adaptateurs de pilotage interchangeables pour le même cas d’utilisation.
<?php
// CLI driving adapter
final class RegisterUserCommand
{
public function __construct(private RegisterUser $register) {}
public function run(array $argv): int {
[$email, $password] = array_slice($argv, 1);
$id = $this->register->handle($email, $password);
fwrite(STDOUT, "Created user $id\n");
return 0;
}
}Adaptateurs en mémoire pour les tests
Le principal avantage est que chaque port piloté reçoit un faux rapide. Les tests exécutent le véritable cas d’utilisation avec des adaptateurs en mémoire, des horloges déterministes et un hacheur qui n’effectue aucune opération.
<?php
final class FixedClock implements Clock {
public function __construct(private DateTimeImmutable $t) {}
public function now(): DateTimeImmutable { return $this->t; }
}
final class PlainHasher implements PasswordHasher {
public function hash(string $p): string { return 'h:' . $p; }
}
$service = new RegisterUserService(
new InMemoryUsers(),
new PlainHasher(),
new FixedClock(new DateTimeImmutable('2026-01-01'))
);
echo 'wired OK', PHP_EOL;Les adaptateurs traduisent, ils ne décident jamais
Une erreur courante consiste à laisser les règles métier s’infiltrer dans les adaptateurs. La règle générale : un adaptateur ne fait que traduire les formats de données et les protocoles. Si vous trouvez un if concernant la tarification, l’éligibilité ou le statut dans un contrôleur ou un dépôt, il appartient au cœur.
- Correspondance JSON ↔ DTO : adaptateur
- Hydratation d’une entité depuis SQL : adaptateur
- « Les VIP bénéficient de 10 % de réduction » : cœur
Un port, plusieurs adaptateurs
Les ports permettent la substitution et même des adaptateurs parallèles. Un NotificationPort peut avoir des adaptateurs pour les e-mails, les SMS et Slack, composés ensemble. Le cœur appelle une seule méthode ; l’assemblage décide du nombre de canaux qui répondent.
<?php
interface Notifier { public function send(string $to, string $msg): void; }
final class CompositeNotifier implements Notifier {
/** @param Notifier[] $channels */
public function __construct(private array $channels) {}
public function send(string $to, string $msg): void {
foreach ($this->channels as $c) $c->send($to, $msg);
}
}
$notifier = new CompositeNotifier([new EmailNotifier(), new SmsNotifier()]);
echo 'composed', PHP_EOL;Comment l’hexagone se traduit en dossiers
Une organisation PHP pragmatique pour un contexte délimité :
Domain/— entités, objets-valeurs, services de domaineApplication/Port/In/— interfaces des ports entrants (cas d’utilisation)Application/Port/Out/— interfaces des ports sortants (dépôts, horloge)Application/— implémentations des cas d’utilisationInfrastructure/Adapter/In/— contrôleurs, CLI, consommateursInfrastructure/Adapter/Out/— adaptateurs PDO/Doctrine/HTTP
La racine de composition (configuration du conteneur DI) relie les adaptateurs In et Out aux ports.
Tester tout l’hexagone
Au-delà des tests unitaires, les ports permettent d’exécuter rapidement des tests d’acceptation qui pilotent l’application par son port principal et vérifient le résultat via des adaptateurs secondaires en mémoire — ce qui couvre un cas d’utilisation complet sans HTTP ni base de données. La même suite de tests peut ensuite s’exécuter avec de vrais adaptateurs lors de tests d’intégration, ce qui vous donne une stratégie de test par couches sans réécriture.
<?php
// Acceptance test: real use case, fake driven adapters, no I/O
$users = new InMemoryUsers();
$service = new RegisterUserService($users, new PlainHasher(),
new FixedClock(new DateTimeImmutable('2026-01-01')));
$id = $service->handle('dev@coddykit.com', 'pw');
assert($users->byId($id) !== null);
echo 'acceptance: user persisted via in-memory adapter', PHP_EOL;Vérification rapide
Quelle affirmation concernant les ports et les adaptateurs est correcte ?
Récapitulatif
Ports et adaptateurs isolent le cœur derrière des interfaces :
- Les ports sont des interfaces exprimées dans le langage du domaine et détenues par le cœur.
- Les ports entrants sont appelés par les adaptateurs entrants ; les ports sortants sont appelés par le cœur et satisfaits par les adaptateurs sortants.
- Les adaptateurs ne font que traduire les protocoles et les formats — ils ne prennent jamais de décisions métier.
- Un même port prend en charge de nombreux adaptateurs (doublures pour les tests, composites pour la diffusion).
- La racine de composition assemble tout ; l’hexagone reste indépendant de tout framework.
Questions Fréquemment Posées
La leçon « Ports et adaptateurs expliqués » est-elle gratuite ?
Oui — le texte complet de « Ports et adaptateurs expliqués » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours PHP Academy, passe à CoddyKit PRO. Le cours PHP Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Ports et adaptateurs expliqués » ?
Isolez le cœur grâce à des ports et à des adaptateurs interchangeables. Tu pratiques PHP Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer PHP Academy ?
Aucune expérience préalable n'est requise. PHP Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 2 sur 4.
Combien de temps prend la leçon « Ports et adaptateurs expliqués » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon PHP Academy ?
Oui. Chaque leçon PHP Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- De l’architecture en couches à l’architecture propre
- Ports et adaptateurs expliqués
- Cas d’utilisation et services applicatifs
- Inversion des dépendances en pratique