Tokyo : Une carte Suica. Un solde. Je monte, je descends, ça débite. Ça marche partout : métro, train, bus, konbini. Depuis 2001.

Paris : Une carte Navigo. Des tickets t+ incompatibles avec les tickets Origine-Destination. Trois apps différentes. Des zones tarifaires. “Vous ne pouvez pas combiner ces titres.”

Même technologie (RFID). Résultats opposés.

C’est exactement comme votre code.


La codebase “Navigo”

public void ProcessOrder(Order order)
{
    if (order.HasDiscount && order.HasLoyaltyPoints)
        throw new Exception("Cannot combine discount and loyalty");

    if (order.IsB2B && order.IsExpress)
        throw new Exception("B2B orders cannot be express");

    // ... 300 lignes de if/else
    // Personne ne sait pourquoi ces règles existent
}

Symptômes :

  • États incompatibles gérés par des exceptions
  • Impossible d’ajouter une feature sans tout casser
  • Les devs ont peur de toucher le code

La codebase “Suica”

public class Account
{
    public Money Balance { get; }

    public Result Debit(Money amount) =>
        amount > Balance
            ? Result.Failure("Insufficient balance")
            : Result.Success(_transactions.Add(amount));
}

public class FareCalculator
{
    public Money Calculate(Station from, Station to) =>
        _pricingPolicy.GetPrice(_distance.Between(from, to));
}

Caractéristiques :

  • Modèle simple qui compose naturellement
  • Règles métier isolées et testables
  • Facile d’évoluer

La vraie différence

  • Contraintes techniques devenues règles métier
  • Legacy impossible à refactorer
  • Chaque feature ajoute un cas spécial

Suica a distillé la complexité essentielle

  • Quel est le vrai problème ? Calculer un tarif
  • Tout le reste est technique, donc isolé
  • Modèle métier minimal et composable

À retenir

Toujours garder ceci à l’esprit : dans 5 ans, personne ne saura pourquoi ces règles existent.

Concentrez-vous sur la complexité essentielle. Isolez le reste. Votre futur vous (et vos collègues) vous remercieront.