Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Architecture Inutile ?

Architecture Inutile ?

Symfony offre au développeur PHP une grande souplesse et une puissance certaine. Malgré cela, comme tout outil, il peut être mal utilisé s'il est mal compris, conduisant parfois des projets dans des situations délicates où l'urgence de la production fait inexorablement gonfler la dette technique. Et cette dette technique, elle se paye à terme beaucoup plus cher que l'investissement de départ !

Dans cette présentation nous mettrons en valeur les vertus d'une architecture logicielle réfléchie et pragmatique, exemples réels à l'appui. Nous verrons les outils que Symfony met à notre disposition pour y arriver, les design patterns mis en œuvre et les pièges à éviter. Les "buzzwords" SOLID, KISS, DRY et autres DDD prendront alors tout leur sens !

Jérôme Vieilledent

March 31, 2017
Tweet

More Decks by Jérôme Vieilledent

Other Decks in Programming

Transcript

  1. Architecture inutile ?

    View Slide

  2. Me, Myself & I
    Jérôme Vieilledent
    CTO

    http://www.code-rhapsodie.fr
    https://github.com/lolautruche
    @jvieilledent
    https://joind.in/talk/b1727

    View Slide

  3. Architecture
    “L'architecture est l'art majeur de concevoir des espaces et de bâtir des
    édifices, en respectant des règles de construction empiriques ou
    scientifiques, ainsi que des concepts esthétiques, classiques ou nouveaux, de
    forme et d'agencement d'espace, en y incluant les aspects sociaux et
    environnementaux liés à la fonction de l'édifice et à son intégration dans
    son environnement, quelle que soit cette fonction.”
    https://fr.wikipedia.org/wiki/Architecture

    View Slide

  4. Architecture
    https://www.flickr.com/photos/[email protected]/9445890440
    https://www.flickr.com/photos/elf-8/15595002523
    https://www.flickr.com/photos/elf-8/15595002523

    View Slide

  5. Architecture logicielle
    “L’architecture logicielle décrit d’une manière symbolique et
    schématique les différents éléments d’un ou de plusieurs
    systèmes informatiques, leurs interrelations et leurs
    interactions. Contrairement aux spécifications produites par
    l’analyse fonctionnelle, le modèle d'architecture, produit lors
    de la phase de conception, ne décrit pas ce que doit réaliser
    un système informatique mais plutôt comment il doit être
    conçu de manière à répondre aux spécifications. L’analyse
    décrit le « quoi faire » alors que l’architecture décrit le «
    comment le faire ».”
    https://fr.wikipedia.org/wiki/Architecture_logicielle

    View Slide

  6. Architecture logicielle
    MERISE UML

    View Slide

  7. Architecture logicielle

    View Slide

  8. Ordres architecturaux

    View Slide

  9. Ordres architecturaux
    Courtesy of Hamida Rebai

    http://hamidarebai.blogspot.fr/2016/03/do-you-like-spaghetti-or-lasagna.html

    View Slide

  10. Equilibrium

    View Slide

  11. Architecture pour quoi faire ?
    Rendre l'édifice SO I
    L DE

    View Slide

  12. Architecture pour quoi faire ?
    S
    O
    I
    L
    D

    View Slide

  13. Architecture pour quoi faire ?
    S
    O
    I
    L
    D
    ingle responsibility principle
    pen/Closed principle
    iskov substitution principle
    nterface seggregation principle
    ependency inversion principle

    View Slide

  14. C'est du solide !
    Photo par Tomasz Scieniki

    View Slide

  15. DRY : C'est mieux quand le ciment est sec !
    D R Y

    View Slide

  16. DRY : C'est mieux quand le ciment est sec !
    D
    R
    Y
    on't
    epeat
    ourself
    https://www.flickr.com/photos/shutterrunner/8187293051

    View Slide

  17. KISS
    I
    K S S

    View Slide

  18. KISS
    I
    K
    S
    S
    eep
    t
    tupidly
    imple
    https://www.flickr.com/photos/[email protected]/17068696485

    View Slide

  19. DU CODE !

    View Slide

  20. Il était une fois, un contexte...
    public static function getContext($attribute = null, $limitContext = null)
    {
    $value = null;
    if (is_null($attribute)) {
    $value = array();
    }
    foreach (self::$CookieContextList as $context) {
    if (!empty($limitContext) && $context != $limitContext) {
    continue;
    }
    if (array_key_exists($context, $_COOKIE)) {
    if (!is_array($_COOKIE [$context])) {
    parse_str($_COOKIE [$context], $_COOKIE [$context]);
    }
    $_COOKIE[$context] =
    array_intersect_key(
    $_COOKIE[$context],
    array_flip(self::$ContextParamsValid[$context])
    );
    if (is_null($attribute)) {
    if (!is_array($value)) {
    $value = array();
    }
    $value = array_merge($value, $_COOKIE [$context]);
    }
    elseif (array_key_exists($attribute, $_COOKIE [$context])) {
    return $_COOKIE [$context] [$attribute];
    }
    }
    }
    return $value;
    }

    View Slide

  21. Il était une fois, un contexte...
    protected static $CookieContextList = array(
    self::CookieContext,
    self::CookieInfo,
    self::CookieSession,
    );
    protected static $CookieContextSessionList = array(
    self::CookieSession,
    );
    /* Définit à quel cookie appartient une information pour la lecture et l'écriture */
    private static $ContextParamsValid = array(
    self::CookieContext => array(
    'cv', // Accessibility => accessible (1) or not
    "reg", // Region
    "mar", // Marché
    ),
    self::CookieInfo => array(
    'typsrv', // Type de DEI
    "nab", // Numero d'abonne crypte
    ),
    self::CookieSession => array(
    "auth", // Connected (WE,WP,WM,WS) or not
    "stub", // Mode bouchon
    "cmar", // Marché de connexion
    "username", // Username (uppercase)
    "cslid",
    "cslname",
    "profil", // hors portefeuille (0), en portefeuille (1) ou client MBL (2)
    "agcid",
    "agcname",
    "agccode",
    ),
    );

    View Slide

  22. Il était une fois, un contexte...

    View Slide

  23. Vous reprendrez bien un peu de contexte ?
    public static function setContext($params, $value = null, $setCookie = true)
    {
    if (is_array($params)) {
    $setCookie = $value;
    }
    if (is_null($setCookie)) {
    $setCookie = true;
    }
    if (!is_array($params)) {
    $params = array($params => $value);
    }
    $params2map = array_intersect_key($params, self::$ContextParamsMapping);
    foreach ($params2map as $key => $value) {
    $paramsKeys = explode(',', self::$ContextParamsMapping[$key]);
    foreach ($paramsKeys as $paramsKey) {
    $params[$paramsKey] = $value;
    }
    }
    // ...
    }

    View Slide

  24. Vous reprendrez bien un peu de contexte ?
    // Format params
    foreach (self::$ContextParamsFormat as $name => $format) {
    if (preg_match_all('/\$([^\$]+)\$/', $format, $result)) {
    foreach ($result[1] as $key => $value) {
    if (!array_key_exists($value, $params)) {
    continue 2;
    }
    $result[1][$key] = $params[$value];
    }
    $format = str_replace($result[0], $result[1], $format);
    }
    if (preg_match_all('/\(([^\)]+)\)/', $format, $result)) {
    foreach ($result[1] as $key => $value) {
    if (preg_match('/:/', $value)) {
    $value = explode(':', $value);
    $value = call_user_func_array(array_shift($value), $value);
    }
    $result[1][$key] = $value;
    }
    $format = str_replace($result[0], $result[1], $format);
    }
    $params[$name] = $format;
    }

    View Slide

  25. Vous reprendrez bien un peu de contexte ?
    // Test pour savoir si l'utilisateur est en portefeuille ou non, Mbl ou non
    // Hors portefeuille (0), en portefeuille (1) ou client MBL (2) ou client EU (3)
    if (isset($params['isMBL']) && isset($params['isPortefeuille'])) {
    if ($params['isMBL'] == "true") {
    $params['profil'] = 2;
    }
    elseif ($params['isPortefeuille'] == "true") {
    $params['profil'] = 1;
    }
    else {
    $params['profil'] = 0;
    }
    }
    if (isset($params['typeAccount']) && strtoupper($params['typeAccount']) == "EU") {
    $params['profil'] = 3;
    }

    View Slide

  26. Vous reprendrez bien un peu de contexte ?
    foreach (self::$CookieContextList as $context) {
    $contextParams = array_intersect_key($params, array_flip(self:$ContextParamsValid[$context]));
    $contextParams = array_merge(self::getContext(null, $context), $contextParams);
    $contextParams = array_map(function ($param) {
    if (!is_null($param) && $param != "") {
    return $param;
    }
    }, $contextParams);
    ksort($contextParams);
    if (!array_key_exists($context, $_COOKIE)) {
    $_COOKIE[$context] = array();
    }
    if ($_COOKIE[$context] != $contextParams || $context == self::CookieContext) {
    $_COOKIE[$context] = $contextParams;
    if ($setCookie) {
    $expiredTime = 0;
    if (!in_array($context, self::$CookieContextSessionList)) {
    $expiredTime = time() + (365 * 24 * 60 * 60);
    }
    setcookie($context, http_build_query($contextParams), $expiredTime, '/');
    }
    }
    }

    View Slide

  27. Vous reprendrez bien un peu de contexte ?

    View Slide

  28. Y a-t-il un architecte dans la salle ?

    View Slide

  29. Conséquences
    • Complexité importante

    • Maintenabilité difficile

    • Évolutivité très risquée


    View Slide

  30. Conséquences
    https://www.flickr.com/photos/jaytamboli/3193232374
    TECHNICAL

    View Slide

  31. Dette technique
    https://www.flickr.com/photos/[email protected]/6757849129

    View Slide

  32. Dette technique
    • $500 milliards en 2010
    • $1000 milliards estimé pour 2015
    • Pas de chiffres plus récents
    • http://www.gartner.com/newsroom/id/1439513

    • Conception négligée => les "intérêts" augmentent
    • Instabilité
    • Corrections introduisant des régressions

    • Dette peut être contrôlée et maîtrisée
    • Création intentionnelle pour accélérer (release)
    • Doit être "remboursée" rapidement (refactoring)

    View Slide

  33. Refactorisation

    View Slide

  34. DDD
    “Nommer, c'est créer, et imaginer, c'est naître.”
    Octavio Paz (poète mexicain, prix Nobel de Littérature 1990)
    Photo : Jonn Leffmann

    View Slide

  35. Définition du domaine
    • Contexte
    • Non unique
    • Lié à la Request (cookie)
    • À disposition du code métier

    • Attributs
    • Informations disponibles dans un contexte
    • Mises à jour possibles
    • Mapping / Filtrage à la mise à jour
    • Règles métier à la mise à jour

    View Slide

  36. Refactorisation
    • Contexte
    • Value object
    • Attributs "propriétés" du contexte

    • ContextFactory
    • Construit les contextes depuis la Request
    • Appelle des builders (1 builder par contexte)

    • ContextProvider
    • Service fournissant contexte/attributs

    View Slide

  37. Refactorisation

    View Slide

  38. Refactorisation

    View Slide

  39. Refactorisation

    View Slide

  40. Bilan
    Avant :
    • 1 classe ContextUtils (~300 lignes)
    • Méthodes statiques
    • Complexité très élevée (indice 48)
    • Évolutivité / extensibilité inexistantes
    • Mélange de couches
    • Impossible à tester unitairement

    View Slide

  41. Bilan
    Après :
    • 21 classes / 6 interfaces
    • ~10-15 lignes par classe
    • Complexité faible (indice moyen 3.45)
    • Extensible et maintenable
    • Testable (et testé !)

    View Slide

  42. Outils utilisés
    • HttpFoundation
    • HttpKernel + EventDispatcher
    • DependencyInjection
    • Définitions de services
    • Compiler passes
    • Design patterns
    • Factory
    • Composite
    • SOLID

    View Slide

  43. Questions ?

    View Slide