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 full-size 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 full-size 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 full-size slide

  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

    View full-size 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 full-size slide

  6. Architecture logicielle
    MERISE UML

    View full-size slide

  7. Architecture logicielle

    View full-size slide

  8. Ordres architecturaux

    View full-size slide

  9. Ordres architecturaux
    Courtesy of Hamida Rebai

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  12. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  17. 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 full-size slide

  18. 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 full-size slide

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

    View full-size slide

  20. 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 full-size slide

  21. 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 full-size slide

  22. 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 full-size slide

  23. 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 full-size slide

  24. Vous reprendrez bien un peu de contexte ?

    View full-size slide

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

    View full-size slide

  26. Conséquences
    • Complexité importante

    • Maintenabilité difficile

    • Évolutivité très risquée


    View full-size slide

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

    View full-size slide

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

    View full-size slide

  29. 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 full-size slide

  30. Refactorisation

    View full-size slide

  31. 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 full-size slide

  32. 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 full-size slide

  33. 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 full-size slide

  34. Refactorisation

    View full-size slide

  35. Refactorisation

    View full-size slide

  36. Refactorisation

    View full-size slide

  37. 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 full-size slide

  38. 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 full-size slide

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

    View full-size slide