Préparation Technique
Engineering Manager @ PrestaShop

Préparation Entretien Technique
PrestaShop Core Engine

PHP/Symfony DDD & CQRS Engineering Manager 30% hands-on
Poste
Engineering Manager
Équipe
5 devs, 1 QA, 1 PM
Stack
PHP/Symfony, Vue.js, Docker
Architecture
DDD, CQRS, SOLID
1

Design Patterns & SOLID

1.1 Les principes SOLID

S - Single Responsibility Principle (SRP)

Définition : Une classe ne doit avoir qu'une seule raison de changer.

Mauvais exemple :

class OrderService
{
    public function createOrder(array $data): Order { /* ... */ }
    public function sendConfirmationEmail(Order $order): void { /* ... */ }
    public function generateInvoicePdf(Order $order): string { /* ... */ }
    public function updateStock(Order $order): void { /* ... */ }
}

Bon exemple :

class OrderService
{
    public function __construct(
        private OrderRepository $orderRepository,
        private StockManager $stockManager,
        private EventDispatcherInterface $dispatcher
    ) {}

    public function createOrder(array $data): Order
    {
        $order = new Order($data);
        $this->orderRepository->save($order);
        $this->stockManager->decreaseStock($order);
        $this->dispatcher->dispatch(new OrderCreatedEvent($order));
        return $order;
    }
}
À retenir : Chaque classe = une responsabilité. Les services annexes (email, PDF) sont délégués via events ou services dédiés.

O - Open/Closed Principle (OCP)

Définition : Ouvert à l'extension, fermé à la modification.

Bon exemple avec Strategy Pattern :

interface DiscountStrategyInterface
{
    public function supports(string $type): bool;
    public function calculate(Order $order): float;
}

class DiscountCalculator
{
    public function __construct(
        /** @var iterable<DiscountStrategyInterface> */
        private iterable $strategies
    ) {}

    public function calculate(Order $order, string $type): float
    {
        foreach ($this->strategies as $strategy) {
            if ($strategy->supports($type)) {
                return $strategy->calculate($order);
            }
        }
        throw new UnsupportedDiscountException($type);
    }
}
À retenir : Utiliser les interfaces et le pattern Strategy. Symfony auto-wire les tagged services.

L - Liskov Substitution Principle (LSP)

Définition : Une classe dérivée doit pouvoir remplacer sa classe parente sans casser le comportement.

Bon exemple :

interface ShapeInterface
{
    public function getArea(): int;
}

class Rectangle implements ShapeInterface
{
    public function __construct(private int $width, private int $height) {}
    public function getArea(): int { return $this->width * $this->height; }
}

class Square implements ShapeInterface
{
    public function __construct(private int $side) {}
    public function getArea(): int { return $this->side ** 2; }
}
À retenir : Préférer la composition à l'héritage. Les interfaces garantissent le contrat.

I - Interface Segregation Principle (ISP)

Définition : Plusieurs interfaces spécifiques valent mieux qu'une interface générale.

interface ProductInterface
{
    public function getName(): string;
    public function getPrice(): float;
}

interface PhysicalProductInterface extends ProductInterface
{
    public function getWeight(): float;
    public function getStock(): int;
}

interface DigitalProductInterface extends ProductInterface
{
    public function getDownloadUrl(): string;
}
À retenir : Segmenter les interfaces par domaine fonctionnel.

D - Dependency Inversion Principle (DIP)

Définition : Dépendre des abstractions, pas des implémentations concrètes.

interface OrderRepositoryInterface
{
    public function save(Order $order): void;
    public function findById(int $id): ?Order;
}

class OrderService
{
    public function __construct(
        private OrderRepositoryInterface $repository // Abstraction injectée
    ) {}
}
À retenir : Injection de dépendances via le constructeur. Symfony s'occupe du wiring.
2

Architecture DDD & CQRS

Domain-Driven Design (DDD)

Concepts clés

  • Entity : Objet avec identité unique qui persiste dans le temps
  • Value Object : Objet défini par ses attributs, immuable
  • Aggregate : Cluster d'objets traité comme une unité
  • Repository : Abstraction de la persistance
  • Domain Service : Logique métier qui ne rentre dans aucune entité

Entity vs Value Object

// Entity - a une identité
class Order
{
    private OrderId $id;
    private Money $total;
    
    public function equals(Order $other): bool
    {
        return $this->id->equals($other->id); // Comparaison par ID
    }
}

// Value Object - pas d'identité, immuable
class Money
{
    public function __construct(
        private readonly float $amount,
        private readonly string $currency
    ) {}
    
    public function equals(Money $other): bool
    {
        return $this->amount === $other->amount 
            && $this->currency === $other->currency; // Comparaison par valeur
    }
    
    public function add(Money $other): self
    {
        return new self($this->amount + $other->amount, $this->currency);
    }
}

CQRS (Command Query Responsibility Segregation)

Principe : Séparer les opérations de lecture (Query) et d'écriture (Command).

Command (Write)

class CreateOrderCommand
{
    public function __construct(
        public readonly int $customerId,
        public readonly array $items
    ) {}
}

class CreateOrderHandler
{
    public function __invoke(CreateOrderCommand $command): void
    {
        // Validation métier
        // Création de l'entité
        // Persistance
        // Événements
    }
}

Query (Read)

class GetOrderQuery
{
    public function __construct(public readonly int $orderId) {}
}

class GetOrderHandler
{
    public function __invoke(GetOrderQuery $query): OrderDTO
    {
        // Lecture optimisée, peut utiliser une vue SQL
        return $this->readModel->getOrder($query->orderId);
    }
}
Avantages CQRS :
  • Optimisation séparée des lectures/écritures
  • Scalabilité différenciée
  • Modèles de données adaptés à l'usage
  • Code plus maintenable
3

PHP/Symfony Spécifique

Symfony Essentials

Dependency Injection

# config/services.yaml
services:
    _defaults:
        autowire: true
        autoconfigure: true
        
    App\:
        resource: '../src/'
        
    # Service avec config
    App\Service\PaymentGateway:
        arguments:
            $apiKey: '%env(PAYMENT_API_KEY)%'

Event System

class OrderCreatedEvent
{
    public function __construct(public readonly Order $order) {}
}

class SendOrderConfirmationListener
{
    public function __invoke(OrderCreatedEvent $event): void
    {
        $this->mailer->send($event->order);
    }
}

Doctrine Tips

  • Toujours vérifier null après find()
  • Toujours appeler flush() après persist()/remove()
  • Utiliser des repositories custom pour les requêtes complexes
  • Éviter le N+1 avec JOIN FETCH
// ✅ Bon
$entity = $this->repository->find($id);
if ($entity === null) {
    throw new NotFoundException();
}

// ❌ Mauvais - pas de vérification null
$entity = $this->repository->find($id);
$entity->doSomething(); // Peut crasher !
4

Testing & Qualité

Pyramide de tests

  • Tests unitaires (70%) : Logique métier isolée, rapides
  • Tests d'intégration (20%) : Interaction entre composants
  • Tests E2E (10%) : Parcours utilisateur complets

PHPUnit - Test Unitaire

class MoneyTest extends TestCase
{
    public function testAddMoney(): void
    {
        $money1 = new Money(10, 'EUR');
        $money2 = new Money(5, 'EUR');
        
        $result = $money1->add($money2);
        
        $this->assertEquals(15, $result->getAmount());
    }
    
    public function testCannotAddDifferentCurrencies(): void
    {
        $this->expectException(CurrencyMismatchException::class);
        
        $euro = new Money(10, 'EUR');
        $dollar = new Money(5, 'USD');
        $euro->add($dollar);
    }
}

Best Practices

  • AAA Pattern : Arrange, Act, Assert
  • Un test = un concept testé
  • Noms explicites : testCannotAddDifferentCurrencies
  • Tests indépendants : pas d'ordre d'exécution
  • Mock les dépendances externes (API, BDD...)
5

Sécurité

OWASP Top 10

1. SQL Injection

// ❌ Vulnérable
$sql = "SELECT * FROM users WHERE email = '" . $_POST['email'] . "'";

// ✅ Sécurisé avec Doctrine
$user = $this->repository->findOneBy(['email' => $email]);

2. XSS (Cross-Site Scripting)

// ❌ Vulnérable
echo "<h1>" . $_GET['name'] . "</h1>";

// ✅ Sécurisé avec Twig
{{ name }} {# Auto-escaped #}

3. CSRF (Cross-Site Request Forgery)

// Symfony Forms auto-gère le CSRF token
$form = $this->createForm(OrderType::class);
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
    // Token CSRF vérifié automatiquement
}
Règles d'or :
  • Ne jamais faire confiance aux inputs utilisateur
  • Toujours valider et sanitizer
  • Utiliser les outils du framework (Forms, ORM...)
  • Principe du moindre privilège (permissions)
6

Open Source & Licences

Licences principales

Licence Permissive ? Usage commercial Modifications
MIT ✅ Oui ✅ Autorisé Propriétaire OK
Apache 2.0 ✅ Oui ✅ Autorisé Protection brevets
GPL v3 ❌ Copyleft ✅ Autorisé Doit rester GPL
AGPL ❌ Copyleft fort ✅ Autorisé + obligation SaaS

Semantic Versioning (SemVer)

Format : MAJOR.MINOR.PATCH (ex: 1.4.2)

  • MAJOR : Breaking changes (incompatibilité)
  • MINOR : Nouvelles features rétro-compatibles
  • PATCH : Bugfixes rétro-compatibles
1.0.0 → 1.0.1 // Bugfix
1.0.1 → 1.1.0 // Nouvelle feature
1.1.0 → 2.0.0 // Breaking change
7

Questions de Posture EM

Comment évalues-tu le niveau technique d'un dev ?

Je l'aborde comme un investissement, pas une corvée :

  1. Code reviews : Qualité du code, patterns utilisés, gestion erreurs
  2. Questions en réunion : Profondeur de compréhension des sujets
  3. Pair programming observé : Comment l'équipe réfléchit
  4. Post-mortems : Les bugs révèlent les faiblesses systémiques
  5. Feedback du Lead : Synchro régulière sur les points d'attention

Comment gères-tu la dette technique ?

  1. Visibilité : Backlog de dette avec impact estimé
  2. Budget dédié : 20% du temps sprint réservé - non négociable
  3. Priorisation : Dette sur le chemin critique en priorité
  4. Boy Scout Rule : Chaque PR laisse le code meilleur
  5. Communication : J'explique au Product que c'est un investissement

Comment simplifies-tu la vie des devs ?

  1. Protéger le focus : Plages de deep work, filtrer les interruptions
  2. Clarifier les specs : Toutes les infos AVANT de coder
  3. Outillage : CI/CD rapide, env de dev fonctionnels
  4. Décisions rapides : Je tranche plutôt que laisser traîner
  5. Reconnaissance : Feedback positif public, critique en privé

Comment gères-tu un désaccord technique ?

  1. Écouter d'abord : Comprendre le raisonnement complet
  2. Challenger avec bienveillance : "As-tu considéré X ?"
  3. Critères objectifs : Évaluer maintenabilité, perf, délai
  4. Accepter de perdre : Si leur argument est meilleur, je le dis
  5. Documenter : ADR pour expliquer le "pourquoi"
8

Questions Pièges Classiques

Questions techniques pièges

Piège : "== vs === en PHP ?"

0 == "0"    // true (conversion de type)
0 === "0"   // false (comparaison stricte)

// Piège classique
0 == "abc"  // true ! ("abc" converti en 0)

À dire : "En PHP moderne, on utilise toujours === sauf cas très spécifique documenté."

Piège : "Pourquoi ce code pose problème ?"

class User
{
    public function __construct(public string $email) {}
}

$user = new User($_POST['email']);

Problèmes :

  • Pas de validation de l'email
  • Pas de sanitization de l'input
  • Pas de gestion si $_POST['email'] n'existe pas
  • En Symfony, on utilise les Form/DTO avec validation

Piège : "Optimiser cette requête ?"

// Afficher les 10 dernières commandes de chaque client
$customers = $customerRepository->findAll();
foreach ($customers as $customer) {
    $orders = $orderRepository->findByCustomer($customer->getId(), limit: 10);
}

Problème : N+1 queries

Solutions :

  • Eager loading avec JOIN
  • Une seule requête avec window function
  • DataLoader pattern

Questions comportementales

"Tu préfères livrer vite ou livrer bien ?"

Ce n'est pas binaire. Je préfère livrer "bien pour le contexte".

  • MVP/Prototype : On accepte plus de dette, validation rapide
  • Feature critique/sécurité : Pas de compromis qualité
  • Feature standard : Tests + code propre, pas de sur-engineering

L'important : alignement équipe sur le niveau de qualité AVANT de coder.

"Pourquoi tu quittes ton poste actuel ?"

Ne jamais critiquer l'employeur actuel

Focus sur ce que tu cherches (pas ce que tu fuis) :

  • Nouveau challenge technique (open source)
  • Contribuer à un projet utilisé par des milliers de marchands
  • Stack qui correspond à tes compétences
9

Checklist Révision Rapide

Avant l'entretien

  • Relire les principes SOLID avec exemples
  • Savoir expliquer DDD (Entité vs VO, Aggregate)
  • Savoir expliquer CQRS (Command vs Query)
  • Connaître les patterns : Repository, Factory, Strategy, Observer
  • Réviser les failles : SQL injection, XSS, CSRF
  • Connaître les licences : MIT, GPL, AGPL
  • Comprendre SemVer et son application
  • Préparer 2-3 anecdotes de gestion d'équipe

Pendant le live coding

  • Lire l'énoncé complètement avant de coder
  • Poser des questions de clarification
  • Verbaliser ton raisonnement à voix haute
  • Commencer simple, refactorer ensuite
  • Typer les paramètres et retours
  • Vérifier les null systématiquement
  • Ne pas oublier flush() avec Doctrine
  • Utiliser l'ORM, pas de SQL brut
  • Si bloqué : dire "Je réfléchis à..." plutôt que silence

Réflexes PHP/Symfony

// Toujours typer
public function method(int $id, string $name): ?Order

// Toujours vérifier null après find()
$entity = $this->repository->find($id);
if ($entity === null) {
    throw new NotFoundException();
}

// Toujours flush() après persist/remove
$this->em->persist($entity);
$this->em->flush();

// Injection par constructeur
public function __construct(
    private readonly ServiceInterface $service
) {}

Questions à poser en fin d'entretien

  1. "Comment l'équipe Core Engine priorise entre les PRs de la communauté et le développement interne ?"
  2. "Quels sont les plus gros challenges techniques de la v9 ?"
  3. "Comment se passe la collaboration avec l'équipe Core Extension ?"
  4. "Qu'est-ce qui ferait de moi un EM qui réussit chez PrestaShop ?"

Vocabulaire technique anglais

Français Anglais
Patron de conception Design Pattern
Injection de dépendances Dependency Injection
Couplage faible Loose coupling
Dette technique Technical debt
Revue de code Code review
Livraison continue Continuous Delivery
Couverture de tests Test coverage
Cas limite Edge case
Goulot d'étranglement Bottleneck
Montée en charge Scaling