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 “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
  2. 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
  3. Architecture pour quoi faire ? S O I L D

    ingle responsibility principle pen/Closed principle iskov substitution principle nterface seggregation principle ependency inversion principle
  4. DRY : C'est mieux quand le ciment est sec !

    D R Y on't epeat ourself https://www.flickr.com/photos/shutterrunner/8187293051
  5. 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; }
  6. 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", ), );
  7. 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; } } // ... }
  8. 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; }
  9. 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; }
  10. 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, '/'); } } }
  11. 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)
  12. 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
  13. 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
  14. 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
  15. 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
  16. Bilan Après : • 21 classes / 6 interfaces •

    ~10-15 lignes par classe • Complexité faible (indice moyen 3.45) • Extensible et maintenable • Testable (et testé !)
  17. Outils utilisés • HttpFoundation • HttpKernel + EventDispatcher • DependencyInjection

    • Définitions de services • Compiler passes • Design patterns • Factory • Composite • SOLID