De l’architecture en couches à l’architecture propre
Comprenez pourquoi les dépendances doivent être orientées vers l’intérieur.
De l’architecture en couches à l’architecture propre est une leçon PHP Academy gratuite sur CoddyKit. Ceci est la leçon 1 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.
Pourquoi l’architecture propre
Vous connaissez déjà la pile PHP classique à trois couches : Contrôleur → Service → Référentiel → Base de données. Elle fonctionne, mais la logique métier finit couplée à Eloquent, Doctrine, à la requête HTTP et au cycle de vie du cadre logiciel. L’architecture propre inverse la direction des dépendances afin que votre domaine ne connaisse rien de l’infrastructure. Le résultat : des cas d’utilisation testables, des adaptateurs interchangeables et une base de code qui résiste aux mises à niveau du cadre logiciel.
La règle des dépendances
L’unique règle de l’architecture propre est la suivante : les dépendances du code source pointent uniquement vers l’intérieur. Les cercles intérieurs (entités, cas d’utilisation) ne doivent jamais référencer les cercles extérieurs (contrôleurs, cadres de mappage objet-relationnel, cadres logiciels). À l’exécution, le flux de contrôle va vers l’extérieur au moyen d’interfaces, mais au moment de la compilation/importation, rien de l’intérieur n’importe quoi de l’extérieur.
- Entités : règles de l’entreprise
- Cas d’utilisation : règles applicatives
- Adaptateurs : contrôleurs, présentateurs, passerelles
- Cadres logiciels et pilotes : base de données, HTTP, Web
Exemple de couches couplées
Voici le type de service que livrent la plupart des applications PHP. Remarquez que la logique du domaine est enchevêtrée avec Eloquent et la réponse HTTP. Vous ne pouvez pas tester unitairement la règle de réduction sans base de données ni cadre logiciel.
<?php
class OrderService
{
public function place(Request $request)
{
$user = User::find($request->user_id); // Eloquent
$total = 0;
foreach ($request->items as $i) {
$total += Product::find($i['id'])->price * $i['qty'];
}
if ($user->is_vip) {
$total *= 0.9; // business rule trapped in infra code
}
Order::create(['user_id' => $user->id, 'total' => $total]);
return response()->json(['total' => $total]);
}
}Entités : domaine indépendant du cadre logiciel
Une entité encode les règles applicables à toute l’entreprise et ne dépend de rien. Du PHP pur, sans annotations ni classe de base provenant du cadre de mappage objet-relationnel. Elle peut être entièrement construite dans un test.
<?php
final class Money
{
public function __construct(public readonly int $cents) {
if ($cents < 0) throw new InvalidArgumentException('negative money');
}
public function multiply(float $factor): self {
return new self((int) round($this->cents * $factor));
}
}
final class Order
{
/** @param array<int,int> $lineCents */
public function __construct(private array $lineCents, private bool $vip) {}
public function total(): Money {
$sum = array_sum($this->lineCents);
$money = new Money($sum);
return $this->vip ? $money->multiply(0.9) : $money;
}
}
echo (new Order([1000, 2000], true))->total()->cents, PHP_EOL; // 2700Les cas d’utilisation pilotent le flux
Un cas d’utilisation (interacteur) orchestre les entités et ne communique avec le monde extérieur qu’au moyen d’interfaces (ports). Il reçoit un objet de transfert de données de requête et renvoie un objet de transfert de données de réponse — jamais un objet HTTP.
<?php
interface OrderRepository {
public function save(Order $order): void;
}
final class PlaceOrder
{
public function __construct(private OrderRepository $orders) {}
public function execute(array $lineCents, bool $vip): int {
$order = new Order($lineCents, $vip);
$this->orders->save($order);
return $order->total()->cents;
}
}La frontière est une interface
Le cas d’utilisation déclare l’interface OrderRepository dont il a besoin. L’interface se trouve dans le cercle intérieur ; l’implémentation concrète Eloquent/Doctrine se trouve à l’extérieur et dépend de l’intérieur. C’est le principe d’inversion des dépendances appliqué à une frontière architecturale.
Direction de la dépendance du code source : EloquentOrderRepository → OrderRepository (interface), jamais l’inverse.
<?php
// Lives in infrastructure layer, points INWARD to the domain interface
final class EloquentOrderRepository implements OrderRepository
{
public function save(Order $order): void {
OrderModel::create(['total' => $order->total()->cents]);
}
}Tester sans infrastructure
Comme le cas d’utilisation dépend d’une interface, les tests injectent un faux. Pas de base de données ni de démarrage du cadre logiciel — des tests unitaires qui s’exécutent en quelques microsecondes et vérifient le comportement métier pur.
<?php
final class InMemoryOrders implements OrderRepository {
public array $saved = [];
public function save(Order $o): void { $this->saved[] = $o; }
}
$repo = new InMemoryOrders();
$useCase = new PlaceOrder($repo);
$total = $useCase->execute([1000, 2000], true);
assert($total === 2700);
assert(count($repo->saved) === 1);
echo "PASS total=$total saved=" . count($repo->saved) . PHP_EOL;Les contrôleurs deviennent des adaptateurs minces
Le contrôleur est désormais un adaptateur : il traduit HTTP en appel de cas d’utilisation, puis le résultat en HTTP. Il ne contient aucune règle métier. Remplacez REST par CLI ou par un processus de traitement de file et le cas d’utilisation reste intact.
<?php
final class OrderController
{
public function __construct(private PlaceOrder $placeOrder) {}
public function store(Request $request): JsonResponse {
$total = $this->placeOrder->execute(
lineCents: $request->input('lineCents'),
vip: (bool) $request->input('vip'),
);
return new JsonResponse(['total' => $total], 201);
}
}Architecture expressive
La structure des dossiers doit exprimer clairement le domaine, et non le cadre logiciel. Évitez Controllers/ et Models/ à la racine. Organisez le code par capability afin qu’une personne qui découvre le projet comprenne ce que fait l’application.
src/Ordering/Domain/— entités, objets-valeurssrc/Ordering/Application/— cas d’utilisation, interfaces de portssrc/Ordering/Infrastructure/— référentiels Eloquent, contrôleurs HTTP
Chaque contexte délimité est un dossier racine ; le cadre logiciel reste aux frontières.
Faire respecter la règle des dépendances
Sans outillage, la discipline s’érode. Utilisez deptrac ou phparkitect dans l’intégration continue afin d’échouer la compilation lorsque le domaine importe l’infrastructure. La règle devient ainsi une garantie à la compilation plutôt qu’un simple espoir lors de la revue de code.
# deptrac.yaml
deptrac:
layers:
- name: Domain
collectors: [{ type: directory, value: src/.*/Domain/.* }]
- name: Application
collectors: [{ type: directory, value: src/.*/Application/.* }]
- name: Infrastructure
collectors: [{ type: directory, value: src/.*/Infrastructure/.* }]
ruleset:
Domain: [] # Domain may depend on nothing
Application: [Domain]
Infrastructure: [Application, Domain]Franchir les frontières avec des objets de transfert de données
Pour empêcher les entités de s’échapper vers l’extérieur, les données qui franchissent une frontière circulent sous la forme d’un simple objet de transfert de données, et non d’une entité ou d’un modèle de mappage objet-relationnel. Le cas d’utilisation renvoie une structure plate que l’adaptateur peut sérialiser ; l’objet du domaine ne quitte donc jamais le cœur et la couche extérieure n’obtient jamais de référence vers l’état interne.
<?php
final class OrderSummary // boundary DTO, no behavior, no domain types
{
public function __construct(
public readonly string $orderId,
public readonly int $totalCents,
) {}
}
final class PlaceOrderV2 {
public function __construct(private OrderRepository $orders) {}
public function execute(array $lineCents, bool $vip): OrderSummary {
$order = new Order($lineCents, $vip);
$this->orders->save($order);
return new OrderSummary('ord_1', $order->total()->cents);
}
}Vérification rapide
Quelle direction de dépendance est autorisée par la règle des dépendances ?
Récapitulatif
Vous êtes passé d’une pile de couches couplées à l’architecture propre :
- La règle des dépendances : les dépendances du code source pointent uniquement vers l’intérieur.
- Les entités contiennent les règles de l’entreprise en PHP indépendant du cadre logiciel.
- Les cas d’utilisation orchestrent le traitement au moyen d’interfaces de ports et renvoient des objets de transfert de données, pas des objets HTTP.
- Les contrôleurs et les référentiels de mappage objet-relationnel sont des adaptateurs extérieurs qui dépendent de l’intérieur (DIP).
- La structure doit exprimer clairement le domaine et des outils comme deptrac font respecter la règle dans l’intégration continue.
Apprends PHP avec un tuteur IA — gratuit
Écris et exécute du vrai code dans ton navigateur, obtiens de l'aide instantanée d'un tuteur IA disponible 24h/24, et reprends là où tu t'es arrêté sur le web ou dans l'app.
- Cours
- 49
- Leçons
- 195
Questions Fréquemment Posées
La leçon « De l’architecture en couches à l’architecture propre » est-elle gratuite ?
Oui — le texte complet de « De l’architecture en couches à l’architecture propre » 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 « De l’architecture en couches à l’architecture propre » ?
Comprenez pourquoi les dépendances doivent être orientées vers l’intérieur. 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 1 sur 4.
Combien de temps prend la leçon « De l’architecture en couches à l’architecture propre » ?
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