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 !

F37e2139d6bde024b7aa7e7306903519?s=128

Jérôme Vieilledent

March 31, 2017
Tweet

Transcript

  1. Architecture inutile ?

  2. Me, Myself & I Jérôme Vieilledent CTO
 http://www.code-rhapsodie.fr https://github.com/lolautruche @jvieilledent

    https://joind.in/talk/b1727
  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
  4. Architecture https://www.flickr.com/photos/75487768@N04/9445890440 https://www.flickr.com/photos/elf-8/15595002523 https://www.flickr.com/photos/elf-8/15595002523

  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
  6. Architecture logicielle MERISE UML

  7. Architecture logicielle

  8. Ordres architecturaux

  9. Ordres architecturaux Courtesy of Hamida Rebai
 http://hamidarebai.blogspot.fr/2016/03/do-you-like-spaghetti-or-lasagna.html

  10. Equilibrium

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

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

  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
  14. C'est du solide ! Photo par Tomasz Scieniki

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

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

    D R Y on't epeat ourself https://www.flickr.com/photos/shutterrunner/8187293051
  17. KISS I K S S

  18. KISS I K S S eep t tupidly imple https://www.flickr.com/photos/131634513@N03/17068696485

  19. DU CODE !

  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; }
  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", ), );
  22. Il était une fois, un contexte...

  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; } } // ... }
  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; }
  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; }
  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, '/'); } } }
  27. Vous reprendrez bien un peu de contexte ?

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

  29. Conséquences • Complexité importante
 • Maintenabilité difficile
 • Évolutivité très

    risquée

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

  31. Dette technique https://www.flickr.com/photos/68751915@N05/6757849129

  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)
  33. Refactorisation

  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
  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
  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
  37. Refactorisation

  38. Refactorisation

  39. Refactorisation

  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
  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é !)
  42. Outils utilisés • HttpFoundation • HttpKernel + EventDispatcher • DependencyInjection

    • Définitions de services • Compiler passes • Design patterns • Factory • Composite • SOLID
  43. Questions ?