Skip to content

Évolution de l'Architecture : Du Service au Manager

Au cours du développement, la structure initiale Controller <-> Service <-> Repository a montré ses limites (Services "fourre-tout"). Un refactoring complet a été opéré pour adopter des patterns plus granulaires.

🏗️ Nouveaux Patterns implémentés

  • Managers : Responsables exclusifs des opérations CRUD et de l'interaction avec les Repositories. Ils portent la logique de persistance (ex: validateAndSave).
  • Mappers : Dédiés à la transformation bidirectionnelle Entity <-> DTO. Cela isole la logique de présentation de la logique de données.
  • Traits : Utilisés pour la réutilisabilité horizontale (ex: gestion automatique des createdAt/updatedAt et factorisation du validateAndSave).
  • Utils : Classes utilitaires pures pour des besoins structurels (génération UUID Base 62, méta-données de pagination).

🧠 Pourquoi ce choix ?

Cette structure réduit drastiquement la dette technique. En séparant les responsabilités, chaque classe devient plus courte, plus lisible et, surtout, beaucoup plus simple à tester unitairement.


📁 Structure du Projet (src/)

Voici l'arborescence complète du répertoire source, issue du refactoring :

src
├── Carrier
│   ├── AbstractCarrier.php
│   └── InternCarrier.php
├── Controller
│   ├── Order
│   ├── Product
│   ├── Shipment
│   └── User
├── DataFixtures
├── Doctrine               ← Custom DQL Functions (ex: UNACCENT)
├── Dto
│   ├── OpenApiModel
│   ├── Request
│   │   └── Filter
│   ├── Response
│   ├── Types
│   └── User
├── Entity
│   ├── Order
│   ├── Product
│   └── User
├── Enum
│   └── SortFilter
├── EventListener
├── Exception
├── Factory                ← CarrierFactory
├── Interface
├── Manager
│   ├── Product
│   ├── Shipment
│   └── User
├── Mapper
│   ├── Product
│   ├── Request
│   ├── Shipment
│   └── User
├── Repository
│   ├── Order
│   ├── Product
│   └── User
├── Response
├── Serializer
│   └── Normalizer
│       ├── Product
│       └── User
├── Service
│   ├── Order
│   ├── Product
│   └── User
├── Trait
├── Util
├── Validator
│   └── Constraints
│       └── Password
└── Voter

Chaque répertoire a une responsabilité unique. Un Manager/Product/ ne touche jamais à la couche Repository/User/ — le couplage est réduit au strict nécessaire.


Factory des Expéditeurs (Carriers)

Pour gérer les différents transporteurs sans répéter de la logique, j'ai mis en place une Factory.

Fonctionnement

On utilise un champ technical_name en base de données (table carrier) qui est mappé sur un Enum PHP. Cela permet de centraliser la logique de création :

  1. La table carrier contient le nom technique (ex: la_poste).
  2. Le backend utilise ce nom pour instancier la bonne logique via la Factory.
  3. Le principe DRY est respecté car toute la gestion des transporteurs passe par ce point unique.