Diagrammes d'architecture as-code avec C4Model : comment ça marche ?

Diagrammes d'architecture as-code avec C4 model : comment ça marche ? (Partie 1)

Remerciement aux relecteurs, et les octos qui ont pratiqué l’exercice du Perfection Game en interne pour aider à l’améliorer : David Benathan, Sofía Calcagno, Hà Hông Viêt LÊ, Sarah Ourabah, David Slimovici

Le métier de concepteur de logiciels est un métier abstrait, ou du moins il nous amène à manipuler quotidiennement des abstractions : des domaines, des contextes, des contrats d’interface, des plateformes et pléthore de systèmes logiciels interagissant entre eux.

Une collaboration efficace pour concevoir ou manipuler ces abstractions passe souvent par le dessin de diagrammes : on projette ces abstractions, ces modèles mentaux qui habitent nos esprits sur une surface de modélisation visuelle, dans l’espoir de les rendre plus concrètes : un tableau blanc physique ou virtuel autour duquel la conversation est facilitée. Néanmoins, encore faut-il arriver à s’accorder sur le sens que prennent les boîtes et les flèches que nous sommes amenés à dessiner.

Slide tirée du talk "AWS re:Invent 2023 - Advanced integration patterns & trade-offs for loosely coupled systems (API309)" de Gregor Hohpe : une boîte et des flèches peuvent avoir de multiples interprétationsExtrait du talk Advanced integration patterns & trade-offs for loosely coupled systems par Gregor Hohpe lors de la conférence AWS Re:Invent 2023 - source

Simon Brown, architecte et conférencier dans les domaines du logiciel et de l'agilité, résume bien sur son site c4model.com à quel point les diagrammes que nous produisons sont souvent difficiles à réutiliser d’une discussion à l’autre :

Ask somebody in the building industry to visually communicate the architecture of a building and you'll be presented with site plans, floor plans, elevation views, cross-section views and detailed drawings.

In contrast, ask a software developer to communicate the software architecture of a software system using diagrams and you'll likely get a confused mess of boxes and lines ... inconsistent notation (colour coding, shapes, line styles, etc), ambiguous naming, unlabelled relationships, generic terminology, missing technology choices, mixed abstractions, etc.

Pour tenter d’apporter de l’ordre dans nos diagrammes d’architecture et éviter de réinventer la roue, il propose un framework de son cru : le C4 model. C'est un framework qui ambitionne de pouvoir modéliser toutes sortes d’architectures, des plus simples aux plus complexes, à l’aide de quatre abstractions principales.

Ces 4 abstractions sont hiérarchiques, et leur nom commence par la lettre C (d'où C4) :

Diagramme/abstraction
A quoi ça sert
Context (C1)
Le diagramme "context" permet de représenter la vue d'ensemble d'un système comme une boîte noire, à haut-niveau:
qui sont les personas, quels systèmes externes interagissent avec, ...
Containers (C2)
Le diagramme "container" permet de zoomer dans un système pour décrire les unités de déploiement qui la composent
Component (C3)
Le diagramme "component" permet de zoomer dans un container pour décrire ses composants et leurs interactions
Code (C4)
Le diagramme "code" permet de zoomer dans un component pour connaître son implémentation, via un diagramme de classe UML ou un schéma relationnel

Illustration des 4 niveaux d'abstractions du C4 model, tirée du site éponyme : https://c4model.com/L’idée est de modéliser le logiciel comme une carte, on peut prendre de la hauteur (dézoomer) ou voir en détails ce qui se passe dans le code (zoomer) - source de l'image

Cet article propose de détailler comment C4 model peut être utilisé dans un projet de développement logiciel, par des exemples de code avec leurs diagrammes correspondants.

⚠️Cet article n’a pas vocation à faire de vous des experts de la modélisation avec ce framework mais à vous aider à mettre le pied à l'étrier de cette modélisation riche en abstractions et en outils. Nous ne rentrerons pas dans le détail de ce qui différencie les niveaux C1 à C4 : ce ne serait qu’une redite du site c4model.com et de la demi-douzaine de conférences de son auteur trouvable sur internet.

Il faut aussi noter que les outils présentés viennent avec un certain coût d’entrée : connaître leur formalisme et arriver à les appliquer n’est pas aussi simple que de dessiner des formes dans un outil de dessin de slides.

Nous vous proposerons dans un autre article des retours d’expérience sur notre utilisation de ce framework de modélisation que nous avons éprouvé dans différents projets.

De l’intérêt de modéliser des diagrammes d’architecture avec du code

Si l’architecte chevronné, habitué à produire ses diagrammes d’architecture avec des slides pourrait trouver farfelu de les modéliser avec du code, la pratique de diagrams-as-code peut avoir quelques avantages certains.

Il devient possible de versionner ses fichiers de diagramme au même titre que du code applicatif ou du code d’infrastructure. Cela peut faciliter le suivi des modifications, la collaboration, le partage des diagrammes et l’appropriation de ceux-ci par les membres d’une équipe de développement.

Comparaison entre deux diagrammes d'architecture versionnésLe jeu des 7 différences, c’est quand même plus simple avec du texte !

Ce versionning peut aussi apporter de l’historisation : on peut voir les diagrammes d’architecture évoluer à mesure qu’on navigue dans l’historique des commits. Il devient aussi possible d’associer un diagramme d’architecture à une version de l’application précise, ce qui est souvent impossible quand les diagrammes et le code évoluent dans des lieux distincts et distants qui possèdent chacun un système de version différent.

Il devient possible d'associer un diagramme à une version du code et de suivre son évolution au cours de la vie de l'application

Il devient possible de créer une synchronisation plus étroite entre le code source d’une application et les diagrammes d’architecture qui le décrivent. Cela permet ainsi d’activer une démarche de Living Documentation, où l’on peut faire évoluer le code et ses diagrammes au même rythme. On augmente ainsi les chances de produire une documentation visuelle alignée avec ce que le code d’une application exprime.

Enfin, le fait de manipuler du code nous offre l’opportunité d’appliquer le principe de séparation de responsabilités : certains outils que nous aborderons par la suite permettent de séparer d’une part des considérations de modélisation (dit de manière crue : définir “les boîtes, les flèches et le sens des flèches”), et les considérations de vue d’autre part (couleur des boîtes, forme des boîtes, taille des boîtes, agencement des boîtes dans l’espace, …).

Ce dernier point permet ainsi de se concentrer sur des discussions et des questions de fond (“à quoi sert cette boîte, pourquoi il y a une flèche ici …”) et laisser pour plus tard des tâches chronophages et à faible valeur ajoutée comme le positionnement des composants au pixel près dans une slide ou leur coloration pour être aligné avec la charte graphique de l’entreprise 🎨

Cette distinction entre modélisation et vue peut paraître déroutante, assez abstraite. Elle sera illustrée en fin d’article au moment de présenter Structurizr, un outil qui offre cette capacité.

Un framework, une panoplie d’outils et d’implémentations

Si vous n’êtes pas perdus jusque là, vous devriez avoir envie de vous essayer à modéliser un diagramme avec C4 Model. Néanmoins, quand Simon Brown a initialement proposé le concept théorique de C4 model, il n'a pas proposé immédiatement d'implémentation ou d’outillage.

Historiquement (à partir de 2018), C4-PlantUML était une des seules implémentations open-source disponible pour produire des diagrammes suivant les abstractions proposées par Simon Brown dans son C4 model.

Mais depuis 2020, Simon Brown propose sa propre implémentation open-source de C4 model avec le framework Structurizr. Pour autant, cette annonce n’a pas entraîné la fin de C4-PlantUML qui reste aujourd’hui plus populaire que Structurizr (à l’écriture de cet article : 1398 ⭐ du côté de Structurizr contre 5800 ⭐ pour C4-PlantUML).

Evolution de la popularité des repos C4-PlantUML et Structurizr/dsl mesurées en étoiles Github: C4-PlantUML est plus populaire, sans ambiguïté

L’existence de plusieurs outils pour pratiquer C4 Model peut ainsi être déroutante pour le débutant ou pour quelqu’un qui se demande quel outil choisir pour pratiquer et collaborer efficacement dans son organisation, d’autant plus que ces deux outils fonctionnent avec des notations différentes.

Pour pratiquer C4 Model, il y a ainsi un choix à faire entre plusieurs outils. Cela peut être Structurizr : un outil plus récent et conçu par le concepteur de C4 Model, ou bien C4-PlantUML : un outil éprouvé par la communauté open-source depuis 6 ans (au moment d’écrire cet article) et que cette même communauté n’a pas abandonné à l’arrivée de Structurizr.

Faire ce choix n’est pas simple et dépend de vos besoins. Un tableau récapitulatif sera disponible en fin d’article avec quelques axes de décisions, mais la meilleure chose à faire est peut-être encore de vous essayer à quelques-uns de ces outils pour vérifier ce qui vous correspond le mieux.

Afin de vous aider à identifier des points différenciant, un même diagramme d’architecture en C4 model sera construit avec 3 outils différents dans cette partie :

Ces outils sont listés, selon moi, du plus simple à prendre en main à celui qui propose le plus d’effort à fournir avant de pouvoir visualiser un diagramme.

Voici un peu de contexte métier sur l’activité de CoolCover Company, un acteur assurantiel majeur tout aussi cool que fictif 😎, pour pouvoir comprendre un même diagramme d’architecture modélisé avec ces différents outils :

  • Dans ce contexte, un souscripteur souhaite assurer son véhicule,
  • Pour souscrire à une assurance, il emprunte un tunnel de souscription B2C,
  • Lors du parcours, un moteur de tarification est interrogé afin de fournir au souscripteur un devis,
  • À la dernière page de formulaire, le tunnel de souscription agrège les données renseignées par le souscripteur accumulées tout au long du parcours pour déclarer une police d’assurance auprès de l’API de souscription,
  • L’API enregistre la police d’assurance dans la base de données, la rendant ainsi active.

Et voici un diagramme décrivant cette histoire, produit avec l’outil Google Slides, sans code, afin d’avoir un premier point de comparaison :

Un diagramme d'architecture produit avec Google Slides, que nous reproduirons avec 3 outils de diagrams-as-code par la suite

Outil #1 MermaidJS

MermaidJS est un outil "diagrams-as-code" générique qui s'est démarqué par son intégration naturelle au très répandu Markdown.

La plupart des interfaces graphiques d'outils de version control (comme Github, Gitlab, ou Azure Devops) permettent une preview web de fichiers Markdown contenant du MermaidJS sans opération de conversion de notre part du format Markdown au format HTML.

Au moment d’écrire cet article, MermaidJS supporte les diagrammes C4 en mode expérimental, en respectant la syntaxe C4-PlantUML que nous verrons par la suite.

Au moment d’écrire cet article, MermaidJS supporte les diagrammes de type C4C (C4 Context), mais en mode expérimental 🦺⚠️

Il suffit d'ouvrir un bloc de code de type mermaid avec l'en-tête C4Context comme suit pour modéliser un diagramme de type context (diagramme C1 dans le tableau en introduction) :

```mermaid
C4Context
  title System Context diagram for Internet Banking System
  … 

Pour modéliser la vue C1 (un diagramme context) avec MermaidJS, il est possible de rédiger le code suivant :

```mermaid
C4Context
  title System Context diagram

  Person(user, "Policyholder", "wants to insure its hoverboard 🛹", $sprite="users")
  System_Ext(pricing, ".NET", "Pricing Engine")

  Enterprise_Boundary(coolCover, "CoolCover Company©") {
      Container(spa, "B2C Funnel SPA", "angular", "Policy subscription tunnel", $sprite="angular")
      Container(api, "Policy distribution API", ".NET", "Registers new policy", $sprite="dotnet")
      ContainerDb(db, "Database", "PostgreSQL", "Holds product, order and invoice information", $sprite="postgresql")
  }

  Rel(user, spa, "subscribes", "https")
  Rel(spa, pricing, "generates a quote", "POST https/json")
  Rel(spa, api, "Issues a new policy", "POST https/json")
  Rel_R(api, db, "Writes")

Si vous copiez le code ci-avant dans un éditeur Markdown capable d’interpréter le MermaidJS, ou bien dans un éditeur de MermaidJS directement comme à cette adresse, vous devriez obtenir le diagramme rendu ci-après :

Diagramme de contexte produit automatiquement à partir de MermaidJS

Outil #2 C4-PlantUML

C4-PlantUML est un projet open-source qui implémente les abstractions de C4 model via des macros PlantUML, un autre langage de "diagrams-as-code" qui se veut générique.

Voici la modélisation (code .puml) et la vue (fichier .png) d'un diagramme context modélisé avec C4-PlantUML :

@startuml System landscape diagram
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml

!define DEVICONS https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/master/devicons
!define FONTAWESOME https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/master/font-awesome-5
!include DEVICONS/angular.puml
!include DEVICONS/dotnet.puml
!include DEVICONS/postgresql.puml
!include FONTAWESOME/users.puml

LAYOUT_WITH_LEGEND()

Person(user, "Policyholder", "wants to insure its hoverboard 🛹", $sprite="users")
System_Ext(pricing, ".NET", "Pricing Engine")

Enterprise_Boundary(coolCover, "CoolCover Company©") {
    Container(spa, "B2C Funnel SPA", "angular", "Policy subscription tunnel", $sprite="angular")
    Container(api, "Policy distribution API", ".NET", "Registers new policy", $sprite="dotnet")
    ContainerDb(db, "Policies database", "PostgreSQL", "Stores events on policies and their state as read model projections", $sprite="postgresql")
}

Rel(user, spa, "subscribes", "https")
Rel(spa, pricing, "generates a quote", "POST https/json")
Rel(spa, api, "Issues a new policy", "POST https/json")
Rel_R(api, db, "Writes", "jdbc")
@enduml

Le même diagramme d'architecture, produit avec du code C4-PlantUML

Si MermaidJS se base sur la notation C4-PlantUML, on notera quelques différences à l’usage.

Pour produire un diagramme à partir de code C4-Plantuml, il est nécessaire d’utiliser la librairie JAR éponyme en ligne de commande, comme décrit dans la documentation.

java -jar plantuml.jar -DRELATIVE_INCLUDE="." ...;

Il existe des outils qui manipulent ce JAR pour vous et qui peuvent vous offrir une live preview du rendu du code, comme l’extension PlantUML dans l’IDE Visual Studio Code. Cette extension peut être une bonne porte d’entrée pour pouvoir s’essayer à C4 Model avec la notation C4-PlantUML en quelques minutes.

L'extension PlantUML de VS Code permet d'obtenir une live preview du code en diagramme

On notera aussi dans l’extrait de code C4-PlantUML ci-avant quelques fonctionnalités non supportées (à ce jour) par MermaidJS, comme l’import de sprites : des images et autres logos de technologies pour agrémenter le diagramme ou la génération automatique d’une légende.

On peut trouver la liste de ces fonctionnalités manquantes à MermaidJS en suivant ce lien, dans la section “unfinished features [...] not supported in the short term”.

Outil #3 - Structurizr-lite

Comme de nombreux termes en informatique, Structurizr souffre de polysémie, voici donc quelques éléments qui risquent dans cet article aussi d’être utilisés de façon interchangeable :

  • Structurizr DSL: un domain-specific language conçu pour manipuler les éléments de C4 model et qui s'organise essentiellement en deux parties :
    • le model pour déclarer les "boîtes", les "flèches" et leurs relations,
    • et les views pour projeter tout ou partie des éléments du model dans des diagrammes.
  • Structurizr Lite : un outil open-source, utilisable en local et qui propose un serveur dans un conteneur docker pour fournir une preview des diagrammes en navigateur,
  • Structurizr: cela peut désigner la version on-premise open-source,
  • Structurizr Cloud Service : une version payante, en SaaS,
  • Structurizr CLI: c’est un outil en ligne de commande, qui permet d’interagir avec le fichier workspace.dsl qui sert de point d’entrée.

Avec Structurizr-Lite, le code se rédige dans un fichier workspace.dsl en respectant le DSL mentionné ci-avant. Ce DSL propose de séparer la modélisation d’un diagramme de son affichage en imposant respectivement un block model pour définir en un seul et même lieu tout le système d’information et un block views pour définir les diagrammes qu’on souhaite construire.

Dans le code qui suit, 3 diagrammes sont définis sur la base de la modélisation de notre application de souscription d’assurance :

workspace {

    model {
        user = person "Policyholder" "wants to insure its hoverboard 🛹" "Person"
        pricingEngine = softwareSystem "Pricing Engine" ".NET" "External System"
        group "CoolCover Company©" {
            b2cFunnel = softwareSystem "B2C subscription Funnel" "Policy subscription tunnel" {
                b2cFunnelSpa = container "B2C subscription SPA" "Angular"
            }
            distribution = softwareSystem "Policy distribution service" "Registers new policy" {
                distributionApi = container "Policy distribution API" ".NET"
                policiesDatabase = container "Policies Database" "Stores events on policies and their state as read model projections" "PostgreSQL" "Database"
            }
        }

        user -> b2cFunnelSpa "subscribes" "https"
        b2cFunnelSpa -> pricingEngine "generates a quote" "https/json"
        b2cFunnelSpa -> distributionApi "issues a new policy" "https/json"
        distributionApi -> policiesDatabase "writes" "jdbc"
    }

    views {
        systemlandscape SystemLandscape "C1 SystemLandscape" {
            include *
            autolayout lr
        }

        container distribution {
            include *
            autolayout lr
        }

        container b2cFunnel {
            include *
            autolayout lr
        }

        theme default

        styles {
            element "Database" {
                shape Cylinder
            }

            element "External System" {
                background #C0C0C0
            }
        }
    }
    
}

Interface de Structurizr-Lite, sur la base du contenu du fichier workspace.dsl

On notera parmi les différences avec C4-PlantUML que Structurizr-Lite ne produit pas de fichiers d’images automatiquement. A la place, cet outil agit comme un serveur qui expose une application web consultable dans un navigateur en lançant la commande suivante en local :

docker run -it --rm -p 8080:8080 -v "$(PWD)/:/usr/local/structurizr/" structurizr/lite;

De fait, installer Docker (ou une solution équivalente) et avoir le droit de lancer un conteneur sur son poste sont des pré-requis pour pouvoir se servir de cette solution.

On peut alors sélectionner le diagramme qu’on veut afficher en plein écran. Sur ce diagramme “System Landscape” qui affiche des diagrammes C1 (context), il est possible de double-cliquer sur le software system “B2C Subscription Funnel” pour passer au diagramme container (C2) pour zoomer à l’intérieur de celui-ci.

Cette fonctionnalité qui peut sembler anodine marque en fait une différence clé entre les outils de diagrams-as-code selon Simon Brown.

Diagrams as code 1.0 vs Diagrams as code 2.0

Il décrit dans cet article "Diagrams as code 2.0" de mai 2021 que les outils comme C4-PlantUML sont de la catégorie “diagrams-as-code 1.0” : ils permettent de créer plusieurs diagrammes, mais qu’il faut maintenir séparément.

Toujours selon lui, Structurizr-lite ouvre la voie du “diagrams-as-code 2.0” : au lieu de créer un fichier par diagramme, on peut modéliser tous les systèmes en un seul lieu puis construire les vues souhaitées à partir de ce modèle.

Conclusion

Si C4 Model apporte des abstractions utiles et un vocabulaire commun bien pratique pour discuter d’architecture, son concepteur nous rappelle dans son talk “Diagrams as Code 2.0” que ce modèle n’a pas de notation propre, la notation est portée par l’outil qui l’implémente.

Différents outils existent pour pratiquer C4 Model, avec une barrière à l’entrée plus ou moins forte

Outil
Notation, pour faire du C4 Model
Pré-requis
In
Out
MermaidJS
C4-PlantUML (toutes les features ne sont pas supportées)
Au moins un outil pour interpréter du Markdown
Du Markdown
Du Markdown interprété
C4-PlantUML
C4-PlantUML
Avoir Java installé, pour exécuter le binaire plantuml.jar
Un fichier .puml
Un fichier de diagramme PNG, SVG, ou LaTeX produit sur le filesystem
Structurizr-Lite
Structurizr DSL
Avoir une solution de conteneurisation installée (comme Docker), un navigateur web, et pouvoir lancer un conteneur
Un fichier workspace.dsl
Des diagrammes consultables dans l’application web structurizr-lite, exportables dans des fichiers PNG ou SVG en cliquant sur des boutons

Si cet article vous a été utile, peut-être serez-vous intéressé par la seconde partie de cet article, à paraître prochainement, dans laquelle nous partagerons quelques questionnements et constats après quelques années d’usages vécus sur le terrain, notamment :

  • Suffit-il d’initier un repo “architecture” et de demander à tout le monde de contribuer pour que la dynamique prenne ?
  • Ou encore : est-ce que cette démarche de modélisation de l’architecture de l’entreprise peut fonctionner dans la pratique avec un “repo central”, loin du repo des développeurs ?