Développer AuditStock ne consiste pas seulement à créer des écrans de gestion de stock. Je dois aussi pouvoir déployer l’application, faire évoluer ses fonctionnalités et comprendre rapidement ce qui se passe chez chaque client.
Un site peut répondre correctement tout en ayant un import bloqué. Une opération peut devenir lente sans provoquer d’erreur. Et lorsque plusieurs instances fonctionnent simultanément, je ne peux pas passer ma journée à les vérifier une par une.
J’ai donc organisé mon architecture autour de quatre éléments complémentaires :
- Le site d’administration AuditStock, déployé dans un conteneur propre à chaque client.
- Un service de distribution des plugins, pour faire évoluer les fonctionnalités.
- BlazorTelemetry, avec un conteneur de télémétrie dédié à chaque instance.
- Health, mon site de surveillance centralisé, accessible à mes partenaires et à moi-même.

La page d’accueil d’AuditStock rassemble les indicateurs métier : maîtrise du stock, valorisation, disponibilité, approvisionnement et service client.
Une vue d’ensemble de mon architecture
Je sépare l’exécution de l’application, la distribution des extensions, l’analyse technique et la surveillance du parc.
flowchart TB
Registry["Registre Docker"]
Plugins["Catalogue de plugins"]
subgraph A["Client A"]
AdminA["Administration AuditStock"]
DataA[("Données et plugins persistants")]
TelemetryA["BlazorTelemetry dédié"]
AdminA --> DataA
AdminA -->|"Logs, traces et métriques"| TelemetryA
end
subgraph B["Client B"]
AdminB["Administration AuditStock"]
DataB[("Données et plugins persistants")]
TelemetryB["BlazorTelemetry dédié"]
AdminB --> DataB
AdminB -->|"Logs, traces et métriques"| TelemetryB
end
Registry -->|"Image applicative"| AdminA
Registry -->|"Image applicative"| AdminB
Plugins -->|"Téléchargement des extensions"| AdminA
Plugins -->|"Téléchargement des extensions"| AdminB
AdminA -->|"Appairage automatique"| Health["Health : surveillance du parc"]
AdminB -->|"Appairage automatique"| Health
Health -->|"Contrôles HTTPS et rapports"| AdminA
Health -->|"Contrôles HTTPS et rapports"| AdminB
Team["Mes partenaires et moi"] --> Health
Diagnostic["Diagnostic assisté via MCP"] --> TelemetryA
Diagnostic --> TelemetryBCette organisation me permet de partager un même socle logiciel tout en conservant une configuration, des extensions et une destination de télémétrie propres à chaque client.
Un socle .NET modulaire
AuditStock repose sur .NET 10 et ASP.NET Core, avec une interface d’administration Blazor Server.
À l’intérieur de l’application, je distingue les composants d’interface, les contrats, les traitements métier et l’accès aux données. Les requêtes métier passent par ChannelMediator, ce qui me permet de structurer les traitements dans des handlers et de conserver les règles métier hors des écrans.
Cette séparation facilite aussi l’observation : lorsqu’une opération pose problème, je peux chercher quel traitement a été exécuté et où le temps a été consommé.
Je conserve ainsi une application modulaire, dont les fonctionnalités peuvent être enrichies par des plugins.
Comment je génère l’image Docker du site d’administration
Pour distribuer le site d’administration, je construis une image Docker à partir du projet AuditStock.AdminWebApp.
Le Dockerfile utilise plusieurs étapes :
- Je prépare l’environnement d’exécution ASP.NET Core et ses dépendances système.
- Je restaure les packages et compile l’application avec le SDK .NET.
- Je publie l’application en configuration Release.
- Je copie le résultat de cette publication dans l’image finale.
Le SDK et les sources utilisés pour construire l’application restent dans les étapes de compilation. L’image finale contient le runtime et les fichiers nécessaires à l’exécution, notamment les dépendances utilisées pour les documents et les PDF.
flowchart LR
Source["Code source"] --> Restore["Restauration des packages"]
Restore --> Build["Compilation Release"]
Build --> Publish["dotnet publish"]
Publish --> Image["Image finale ASP.NET Core"]
Image --> Registry["GitHub Container Registry"]
Registry --> Deploy["Déploiement des instances clients"]
Secret["Secret de build"] -.-> RestoreL’accès aux packages privés passe par un secret de build Docker. Je n’ai pas besoin d’inscrire ce secret dans le Dockerfile.
Le workflow GitHub Actions publie l’image avec un identifiant de build et une étiquette d’environnement, comme staging ou latest. Je peux ainsi identifier l’artefact produit et sélectionner l’image à déployer.
Je distingue bien ces deux opérations : publier une image dans le registre prépare sa distribution ; déployer cette image met effectivement à jour une instance.
Une image commune, une configuration par client
Je ne reconstruis pas toute l’application pour chaque client. Je réutilise l’image et fournis au déploiement les paramètres propres à l’instance.
Les fichiers persistants, les répertoires de plugins et la configuration ne doivent pas dépendre de la durée de vie du conteneur. Les volumes permettent de conserver ces éléments lorsque je remplace celui-ci.
Devant les instances, Traefik assure le routage par nom de domaine et l’exposition HTTPS.
Un hébergement centralisé pour distribuer les plugins
Les clients n’ont pas tous les mêmes besoins. Certains utilisent un connecteur spécifique, d’autres des fonctions d’analyse ou des extensions métier.
Pour gérer cette diversité, je dispose d’un service dédié : AuditStock.PluginApi.
Ce service possède sa propre image Docker et son stockage persistant. Il héberge le catalogue et les packages des plugins, avec un accès à l’API protégé par clé.
Je distingue ici deux responsabilités :
- Le service de plugins héberge et distribue les packages.
- L’instance AuditStock charge et exécute les plugins installés.
Un plugin téléchargé peut ainsi enrichir les menus, les écrans ou les traitements de l’application du client.
flowchart LR
Code["Code du plugin"] --> Package["Package et métadonnées"]
Package --> Publisher["Outil de publication"]
Publisher --> API["PluginApi"]
API --> Local["Téléchargement et préparation"]
Local --> Startup["Application au démarrage"]
Startup --> Admin["Chargement dans AuditStock"]Au démarrage, l’application recherche les mises à jour des plugins installés et vérifie leur compatibilité avec sa version. Les modifications des fichiers interviennent avant le chargement des assemblies.
J’ai également prévu une limite de temps pour cette recherche : si le catalogue distant ne répond pas, l’application peut continuer son démarrage avec les plugins disponibles localement.
Un conteneur BlazorTelemetry dédié à chaque instance
Chaque instance AuditStock envoie sa télémétrie vers son propre conteneur BlazorTelemetry.
Je conserve ainsi un périmètre de diagnostic clair : lorsque j’analyse un incident chez un client, je consulte la destination de télémétrie associée à son instance.

Le tableau de bord BlazorTelemetry me permet de rapprocher l’activité applicative de la consommation des ressources. Les informations d’identification ont été floutées.
La collecte repose sur OpenTelemetry et le protocole OTLP. Elle rassemble trois types de signaux complémentaires.
| Signal | Ce que j’y cherche |
|---|---|
| Logs | Les événements, avertissements et erreurs qui expliquent le déroulement d’une opération. |
| Traces | Le parcours d’une requête et la durée de ses différentes étapes. |
| Métriques | L’évolution des performances, des volumes et des ressources dans le temps. |
L’instrumentation couvre notamment ASP.NET Core, les appels HTTP, le runtime, Entity Framework et les handlers ChannelMediator.
Suivre les requêtes et comprendre les lenteurs
La vue des requêtes me permet d’examiner l’activité HTTP : méthode utilisée, statut de réponse, durée et lien vers la trace correspondante.

Je peux repérer une requête lente ou en erreur, puis poursuivre l’analyse à partir de sa trace. Les routes et les adresses IP sont floutées.
Si une opération devient lente, je peux examiner sa trace, repérer un traitement coûteux, puis consulter les logs corrélés. Les métriques me permettent ensuite de déterminer si le phénomène est ponctuel ou récurrent.

La chronologie d’une trace rend visibles les traitements et leurs durées. Je peux suivre les handlers métier et localiser les étapes qui méritent une investigation.
Interroger la télémétrie avec un serveur MCP
BlazorTelemetry expose également un serveur MCP, protégé par une clé dédiée.
Il permet à un assistant autorisé de consulter les données de diagnostic en lecture seule : rechercher des erreurs, filtrer les logs, retrouver une trace et ses événements associés, ou examiner une série de métriques.
Je peux ainsi formuler des demandes comme :
« Recherche les erreurs de cette instance sur la dernière heure et retrouve les traces associées. »
Ou :
« Examine les traces les plus lentes sur cette période pour identifier les opérations concernées. »
Le MCP fournit un accès structuré aux observations. Je peux m’en servir pour accélérer une investigation, puis vérifier les hypothèses à partir des traces et des mesures disponibles.
Health : voir l’état de tous mes clients sur un seul écran
BlazorTelemetry me donne le détail technique d’une instance. Avec Health, je cherche d’abord à répondre à une autre question :
Quel client nécessite mon attention maintenant ?
Le site de surveillance centralise les instances dans un tableau de bord commun. Mes partenaires et moi pouvons y repérer les incidents sans ouvrir chaque application séparément.

Health rassemble la disponibilité, la latence, l’état des imports, la taille des bases, les versions et la date du dernier rapport. Les noms, logos et URL des clients sont floutés.
L’accès repose sur des comptes individuels et une authentification par identifiant et code temporaire TOTP. Je peux attribuer des comptes lecteurs aux partenaires qui ont besoin de consulter l’état du parc.
Un appairage automatique des instances
Lorsque la fonction est activée en production, l’instance AuditStock lance automatiquement son appairage avec Health au démarrage.
Health authentifie la demande, puis rappelle l’instance sur son endpoint de santé. Cette vérification permet de contrôler son identité et son état avant de l’enregistrer dans la surveillance.
sequenceDiagram
participant Instance as Instance AuditStock
participant Health as Site Health
participant Dashboard as Tableau de bord
Instance->>Health: Demande d’appairage authentifiée
Health->>Instance: Vérification de l’identité et de l’état
Instance-->>Health: Réponse de santé
Health->>Dashboard: Enregistrement ou actualisation
loop Surveillance périodique
Health->>Instance: Contrôle de disponibilité
Instance-->>Health: État courant
Health->>Instance: Collecte du rapport
Instance-->>Health: Informations de suivi
Health->>Dashboard: Actualisation des indicateurs
endSi le service de surveillance est temporairement inaccessible, l’instance réessaie avec un délai progressif.
Je réduis ainsi les opérations manuelles nécessaires pour intégrer une nouvelle instance au tableau de bord.
Des alertes adaptées aux différents problèmes
Je surveille plusieurs situations, car une application accessible peut malgré tout rencontrer un problème métier.
Dans Health, je distingue trois catégories d’incidents :
| Incident | Détection prévue par défaut |
|---|---|
| Indisponibilité | Trois échecs consécutifs des contrôles effectués chaque minute. |
| Échec de collecte | Trois échecs consécutifs de récupération des rapports, collectés toutes les cinq minutes. |
| Retard d’import | Absence de mise à jour au-delà de deux heures de temps surveillé pour les familles d’import suivies. |
Le suivi des imports couvre les ventes, les livraisons, les achats et les réceptions.
Je tiens compte des plages de surveillance : par défaut, du lundi au vendredi, de 8 h à 18 h, dans le fuseau Europe/Paris. Un arrêt normal des échanges pendant la nuit ne doit pas être traité comme un blocage en pleine journée.
Lorsqu’une instance est indisponible, je suspends les alertes de retard d’import jusqu’à la réception d’un rapport valide. Cela évite de multiplier les alertes secondaires pour une même panne.
Tableau de bord, e-mails et alertes techniques
Health affiche les incidents dans le tableau de bord et envoie les notifications par e-mail via Brevo. Le cycle comprend une alerte initiale, des rappels périodiques — toutes les quatre heures par défaut — et une notification de rétablissement.
BlazorTelemetry complète ce dispositif avec des règles techniques : erreurs dans les logs, taux d’erreur des traces, latence au percentile 95, dépassement d’un seuil de métrique ou absence de télémétrie.
Selon la configuration, ces notifications techniques peuvent être envoyées par webhook, SMTP ou ntfy.
Je dispose donc de deux niveaux complémentaires : la surveillance opérationnelle du parc et les alertes détaillées sur le comportement de chaque application.
De l’alerte au diagnostic
Imaginons qu’un client ne reçoive plus ses données d’achat.
L’application peut rester accessible. Le problème apparaîtra pourtant dans Health lorsque le retard d’import dépassera le seuil prévu.
Je peux alors ouvrir la télémétrie de cette instance, rechercher les erreurs du connecteur, examiner les traces concernées et utiliser le serveur MCP pour faciliter l’analyse.
flowchart LR
Detection["Health détecte un retard"] --> Alert["Tableau de bord et e-mail"]
Alert --> Investigation["Consultation de BlazorTelemetry"]
Investigation --> MCP["Recherche assistée via MCP"]
MCP --> Cause["Identification de la cause"]
Cause --> Action["Intervention"]
Action --> Recovery["Contrôle du rétablissement"]C’est cette continuité que je recherche dans l’architecture d’AuditStock : pouvoir distribuer une version, adapter les fonctionnalités à chaque client, détecter les anomalies et disposer des éléments nécessaires pour les comprendre.
À mesure que le parc grandit, je veux que mes partenaires et moi conservions une vue lisible de son état, tout en pouvant descendre jusqu’au détail d’une opération lorsque la situation l’exige.
Aucun commentaire publié pour le moment.
Ajouter un commentaire