Le signal le plus important ne venait pas de mon infrastructure : il venait des clubs. Leurs retours n’étaient pas bons. Ils trouvaient le service trop compliqué, notamment parce qu’ils devaient jongler entre plusieurs sites pour gérer leur activité. De leur côté, les fournisseurs ne jouaient pas vraiment le jeu pour mettre en place le système de marge arrière sur lequel reposait une partie de mon modèle économique. Tous ces signaux m’ont décidé à simplifier StockAsso en profondeur et à chercher une nouvelle architecture, plus lisible pour les utilisateurs comme pour moi. Quand le terrain et la technique commencent à froncer les sourcils en même temps, je finis généralement par écouter.
Lorsque nous avons lancé StockAsso en avril 2024, je connaissais déjà bien Docker, mais je n’étais pas encore à l’aise avec toute la pile Linux et Ubuntu. Mon environnement naturel restait Windows : j’avais conçu l’hébergement pour des serveurs Windows avec IIS, un système de déploiement interne nommé Palace pour mes microservices et un second outil, ClustIIS, pour déployer mes sites dans IIS.
J’ai d’abord fusionné Palace avec ClustIIS. J’ai ensuite porté ClustIIS sur Linux, avec un serveur central et des agents de déploiement. Cette évolution m’a permis d’abandonner progressivement les serveurs Windows au profit de VPS Linux.
Le résultat technique était intéressant, mais je passais encore beaucoup trop de temps sur la tuyauterie et pas assez sur le produit final. Je développais l’outil qui déployait l’outil qui déployait StockAsso. À ce rythme, j’allais bientôt devoir développer un outil pour savoir quel outil je devais développer.
J’ai donc fini par supprimer ClustIIS au profit de Docker et Portainer. Je comprends vite, mais il faut m’expliquer longtemps.
flowchart LR
Launch["Avril 2024<br/>Je lance StockAsso"]
Windows["Windows + IIS<br/>Palace + ClustIIS"]
Merge["Je fusionne Palace<br/>avec ClustIIS"]
Linux["Je porte ClustIIS sur Linux<br/>Serveur + agents de déploiement"]
VPS["J’abandonne Windows<br/>au profit de VPS Linux"]
Docker["Je supprime ClustIIS<br/>Docker + Portainer"]
V2["Je recentre mon énergie<br/>sur StockAsso2"]
Launch --> Windows --> Merge --> Linux --> VPS --> Docker --> V2Cette transformation s’est étalée sur environ deux ans, le temps pour moi d’assimiler réellement la pile Linux. Je la trouve aujourd’hui formidable : simple, robuste, automatisable et particulièrement adaptée à ce que je veux construire avec StockAsso2. J’ai dû parcourir quelques kilomètres de ligne de commande avant d’arriver à cette conclusion, mais le paysage valait le détour.
En parallèle, j’ai dû intégrer une autre évolution beaucoup plus rapide : celle de l’IA agentique. Depuis environ un an, je la vois progresser de manière spectaculaire, parfois d’une semaine à l’autre. Elle ne change pas seulement ma façon d’écrire du code ; elle change aussi ma manière de formaliser mes règles, de découper mes tâches et de faire évoluer une application existante.
Cette double progression — ma maîtrise de Linux d’un côté, l’arrivée des agents IA de l’autre — a fortement influencé StockAsso2. Elle m’a permis de simplifier l’infrastructure tout en accélérant la transformation du code. Pour une fois, j’ai pu retirer de la complexité au lieu de lui trouver un nouveau nom de projet.
J’ai longtemps fait évoluer StockAsso comme un SaaS multi-tenant centralisé, découpé en plusieurs applications web et plusieurs microservices orchestrés avec .NET Aspire. Sur le papier, l’ensemble cochait beaucoup de cases d’une architecture moderne. Dans la réalité, je déployais, surveillais et faisais communiquer une petite armée de processus pour permettre à des clubs de vendre des maillots et de gérer leurs commandes.
À un moment, je me suis donc posé une question très technique : est-ce que j’avais réellement besoin de toute cette plomberie ?
Ma réponse a été non.
J’ai remplacé cette architecture par une approche plus simple : un monolithe modulaire organisé en Vertical Slices, déployé dans un conteneur Docker isolé pour chaque club, avec une base SQLite dédiée.
Je n’ai pas abandonné la modularité. J’ai abandonné le coût de sa distribution. Nuance importante : j’ai gardé les frontières métier, mais j’ai cessé de leur louer un appartement séparé.
Ma V1 : des microservices centralisés et un SaaS multi-tenant
Dans la V1, j’avais séparé l’application en plusieurs interfaces web et plusieurs services métier. Tous les clubs partageaient la même plateforme, la même infrastructure et une base SQL Server centralisée, avec une séparation logique par AssociationId.
Dans cette vue d’ensemble, je vois immédiatement les trois applications web, la couche d’API, l’orchestration Aspire, les cinq services métier et les dépendances externes. Je vois aussi le point de convergence de l’ensemble : ma base SQL Server centralisée.
flowchart TB
Users["Membres et administrateurs"]
subgraph WebApps["Mes applications web V1"]
Front["FrontWebApp<br/>Boutique publique"]
Admin["AdminWebApp<br/>Administration des clubs"]
Appro["ApproWebApp<br/>Approvisionnement et stocks"]
end
Gateway["PublicApi + ApiClient"]
subgraph Aspire["Mon orchestration .NET Aspire"]
Account["AccountSvc"]
Catalog["CatalogSvc"]
Common["CommonSvc"]
Purchase["PurchaseSvc"]
Sale["SaleSvc"]
end
Database[("SQL Server centralisé<br/>Tous mes clubs en multi-tenant")]
Blob["Azure Blob Storage"]
Payments["Prestataires de paiement"]
Emails["Services d’e-mails"]
Users --> WebApps
WebApps --> Gateway
Gateway --> Aspire
Aspire --> Database
Aspire --> Blob
Aspire --> Payments
Aspire --> EmailsCette architecture avait des qualités. J’avais des domaines métier visibles, des responsabilités séparées et une plateforme capable d’accueillir plusieurs clubs.
Mais j’en payais aussi le prix :
- je devais orchestrer plusieurs services et plusieurs applications ;
- je devais maintenir leurs contrats et leurs communications ;
- je dépendais d’une base SQL Server centralisée et coûteuse ;
- je multipliais les points de défaillance et les éléments à superviser ;
- je faisais porter à chaque évolution fonctionnelle le poids de l’architecture entière.
J’avais parfois l’impression d’avoir engagé un orchestre symphonique pour jouer la sonnerie d’un Nokia 3310. Le résultat fonctionnait, mais le ratio entre le besoin métier et l’infrastructure commençait à me gratter sérieusement.
Mon changement de perspective : un club devient une unité de déploiement
Le changement le plus important n’a pas été technique. J’ai d’abord changé ma manière de définir l’isolation.
Dans la V1, je regroupais tous les clubs dans une plateforme commune et je séparais leurs données par un identifiant. Dans la V2, j’isole physiquement chaque club dans sa propre instance :
- je déploie un conteneur Docker par club ;
- je fournis dans ce conteneur un seul site web pour la boutique et l’administration ;
- j’associe à chaque instance sa propre base SQLite sur un volume persistant ;
- je stocke également les images dans SQLite sous forme de BLOB ;
- je peux sauvegarder ou déplacer l’essentiel d’un club avec son fichier de base de données.
flowchart LR
Internet["Internet"] --> Proxy["Reverse proxy"]
Proxy --> ClubA
Proxy --> ClubB
Proxy --> ClubC
subgraph ClubA["Mon instance du club A"]
WebA["StockAsso2.WebApp<br/>Boutique + administration"]
DbA[("SQLite<br/>club-a.db")]
WebA --> DbA
end
subgraph ClubB["Mon instance du club B"]
WebB["StockAsso2.WebApp<br/>Boutique + administration"]
DbB[("SQLite<br/>club-b.db")]
WebB --> DbB
end
subgraph ClubC["Mon instance du club C"]
WebC["StockAsso2.WebApp<br/>Boutique + administration"]
DbC[("SQLite<br/>club-c.db")]
WebC --> DbC
endJ’ai donc supprimé le multi-tenant à l’intérieur de l’application. Je conserve une plateforme capable de servir plusieurs clubs, mais je déplace la séparation au niveau du déploiement. Je préfère désormais trois petites boîtes étanches à une grande boîte remplie de séparateurs et d’étiquettes « surtout ne pas mélanger ».
Étape 1 : j’ai copié la V1 et renommé StockAsso en StockAsso2
Pour démarrer la conversion, j’ai choisi une stratégie volontairement simple : j’ai recopié tout le code legacy de la V1 dans un nouveau répertoire, puis j’ai remplacé globalement le namespace racine StockAsso par StockAsso2.
flowchart LR
V1["Code legacy V1<br/>namespace StockAsso"]
Copy["Copie dans un nouveau répertoire"]
Rename["Renommage global du namespace"]
V2["Base de migration<br/>namespace StockAsso2"]
Search["Recherche des références StockAsso restantes"]
V1 --> Copy --> Rename --> V2 --> Search
Search -. "Une occurrence restante attire mon attention" .-> V2Ce renommage n’était pas une refonte magique en un clic. Il me donnait surtout un marqueur très efficace : si je retrouvais encore StockAsso dans le nouveau code, je savais immédiatement qu’un morceau de la V1 s’était caché sous le tapis.
J’ai ainsi séparé deux problèmes : d’abord rendre le code identifiable et compilable dans son nouvel espace, ensuite le transformer progressivement. J’ai évité le « grand soir » où je réécris tout avant de découvrir, six mois plus tard, qu’un if apparemment anodin contenait en réalité quinze ans de jurisprudence métier.
Étape 2 : j’ai transformé mes décisions en règles avec AGENTS.md
À partir du code existant, j’ai rédigé un énorme fichier AGENTS.md. Le mot énorme n’est pas une figure de style : j’ai commencé par écrire un guide, puis j’ai manifestement rédigé une constitution.
J’y ai inscrit les règles métier et techniques qui devaient guider toute la conversion, y compris le travail réalisé avec Codex.
1. J’ai simplifié le déploiement
J’ai remplacé la plateforme centralisée multi-tenant par une architecture décentralisée : chaque club reçoit son conteneur Docker et sa base SQLite. J’obtiens une isolation naturelle des données, une infrastructure plus lisible et un coût de base de données qui tombe à 0 €.
2. J’ai fusionné la boutique et l’administration
J’ai regroupé le site public et la console d’administration dans StockAsso2.WebApp. Je déploie désormais un seul site par club au lieu de coordonner plusieurs frontaux.
Cette fusion ne signifie pas que je mélange les responsabilités. Je conserve des routes, des pages, des autorisations et des modules distincts. Je partage simplement le même processus, ce qui est beaucoup moins bavard qu’une réunion permanente entre conteneurs.
3. J’ai simplifié le modèle produit
J’ai abandonné les regroupements de produits, les propriétés dynamiques et les mécanismes d’héritage d’attributs. Je travaille maintenant avec un Product unique et des options directes.
Je perds une partie de la généricité théorique, mais je gagne un modèle que je peux expliquer sans tableau blanc, laser et ravitaillement.
4. J’ai rendu le modèle économique prévisible
J’ai remplacé les commissions arrière variables par une commission fixe de 1 € pour chaque produit vendu, et non de 1 € par commande. Je m’appuie sur Stripe pour prélever automatiquement cette commission lors du paiement : si une commande contient trois produits, la commission est donc de 3 €.
Je peux ainsi expliquer le tarif en une phrase. Pour un modèle économique, c’est presque une fonctionnalité premium.
5. J’ai prévu la migration au lieu de l’espérer
J’ai créé un outil dédié pour extraire les données de la V1, les transformer vers le modèle simplifié de la V2, puis les injecter dans la base SQLite de chaque club.
flowchart LR
SqlServer[("SQL Server V1<br/>Données centralisées")]
Extract["J’extrais les données<br/>du club ciblé"]
Transform["Je transforme les données<br/>vers le modèle V2"]
Inject["J’injecte les données<br/>dans la nouvelle instance"]
SQLite[("SQLite V2<br/>Base dédiée au club")]
SqlServer --> Extract --> Transform --> Inject --> SQLiteJe traite ainsi la migration comme un produit à part entière, avec ses règles et ses tests, et non comme un script héroïque lancé un vendredi à 17 h 58.
6. J’ai placé le mobile en premier
J’ai posé une règle simple : tout doit être réalisable depuis un smartphone. Je ne veux pas seulement que les pages « rentrent » sur un petit écran. Je veux que le catalogue, l’administration, le panier, le paiement et les opérations courantes restent réellement utilisables au pouce.
Cette contrainte influence la navigation, la taille des actions, l’ordre des informations, les formulaires et le tunnel de commande. Elle explique aussi pourquoi la partie web design a demandé autant de temps. Le dernier détail CSS possède une capacité fascinante à manger une demi-journée tout en ayant l’air innocent.
Mon monolithe est modulaire, pas monolithique dans sa conception
J’ai conservé mes grands domaines métier : compte, catalogue, vente, achat et fonctions communes. Les projets StockAsso2.AccountApi, StockAsso2.CatalogApi, StockAsso2.SaleApi, StockAsso2.PurchaseApi et StockAsso2.CommonApi restent des bibliothèques métier chargées dans le même processus. Je ne les déploie plus comme des microservices indépendants.
StockAsso2.WebApp constitue le point d’entrée. StockAsso2.Core compose l’application. Les modules communiquent en mémoire au moyen de ChannelMediator, tandis que StockAsso2.Contracts porte les contrats, StockAsso2.Datas les entités métier et StockAsso2.EntityFramework l’accès partagé à SQLite.
Dans cette seconde vue, je conserve mes domaines métier, mais je les rassemble dans le même processus. Je remplace les appels entre services par des Requests et des Notifications en mémoire, et je fais converger la persistance vers une base SQLite propre au club.
flowchart TB
Web["StockAsso2.WebApp<br/>Blazor Server / SSR + Minimal API"]
Core["StockAsso2.Core<br/>Point de composition"]
subgraph Modules["Mes modules métier dans un seul processus"]
Account["Account<br/>Membres, associations, fournisseurs"]
Catalog["Catalog<br/>Produits, marques, stocks"]
Sale["Sale<br/>Paniers, commandes, paiements"]
Purchase["Purchase<br/>Devis, achats, réceptions"]
Common["Common<br/>Messages, documents, configuration"]
end
Mediator["ChannelMediator<br/>Requests + Notifications en mémoire"]
Contracts["StockAsso2.Contracts<br/>Contrats partagés"]
Datas["StockAsso2.Datas<br/>Entités métier"]
Ef["StockAsso2.EntityFramework<br/>DbContext partagé"]
Db[("StockAsso2.Sqlite<br/>stockasso2.db")]
Web --> Core
Core --> Account
Core --> Catalog
Core --> Sale
Core --> Purchase
Core --> Common
Account --> Mediator
Catalog --> Mediator
Sale --> Mediator
Purchase --> Mediator
Common --> Mediator
Mediator --> Contracts
Mediator --> Datas
Contracts --> Ef
Datas --> Ef
Ef --> DbJe garde donc des frontières métier visibles, mais je remplace les appels réseau par des appels en mémoire et je partage le même contexte de données. Je réduis fortement la latence, la configuration, la supervision et le nombre de scénarios de panne.
Je peux toujours extraire un module plus tard si un besoin réel le justifie. Cette fois, je demanderai toutefois au besoin de présenter une pièce d’identité et trois justificatifs avant de créer un nouveau microservice.
Comment je distingue réellement les deux approches
Je ne compare pas seulement « beaucoup de projets » à « un seul projet ». Dans les deux versions, je garde plusieurs projets et des domaines métier séparés. La vraie différence concerne l’unité de déploiement, le mode d’isolation et le coût des communications.
Dans ma V1, mon unité de déploiement était le service et mon unité d’hébergement était la plateforme partagée. Dans ma V2, mon unité de déploiement devient l’instance complète d’un club. J’ai déplacé la frontière : elle ne passe plus entre chaque domaine métier, elle passe désormais entre les clubs.
flowchart LR
subgraph V1["Ma V1 : je distribue les domaines"]
V1Account["AccountSvc"]
V1Catalog["CatalogSvc"]
V1Sale["SaleSvc"]
V1Shared[("SQL Server partagé")]
V1Account --> V1Shared
V1Catalog --> V1Shared
V1Sale --> V1Shared
end
subgraph V2["Ma V2 : je distribue les clubs"]
Club1["Monolithe modulaire<br/>Club 1"] --> Db1[("club1.db")]
Club2["Monolithe modulaire<br/>Club 2"] --> Db2[("club2.db")]
Club3["Monolithe modulaire<br/>Club 3"] --> Db3[("club3.db")]
end| Dimension | Mon approche V1 | Mon approche V2 | La différence pour moi |
|---|---|---|---|
| Frontière principale | Je séparais les domaines en services. | Je sépare physiquement les clubs. | Je privilégie maintenant l’isolation des clients à l’isolation réseau des modules. |
| Déploiement | Je déployais plusieurs applications et services pour toute la plateforme. | Je déploie une application complète par club. | Je simplifie une instance, mais je dois gérer une flotte d’instances. |
| Communication | Je passais par des API et des contrats réseau. | Je passe par ChannelMediator en mémoire. | Je supprime de la latence et de nombreux scénarios d’erreur distribuée. |
| Données | Je centralisais tous les clubs dans SQL Server avec AssociationId. |
Je donne un fichier SQLite à chaque club. | Je remplace l’isolation logique par une isolation physique. |
| Montée en charge | Je pouvais théoriquement dimensionner chaque service séparément. | Je dimensionne l’instance complète du club. | Je perds en granularité de scaling, mais mon besoin actuel ne la justifie pas. |
| Périmètre d’une panne | Une panne partagée pouvait toucher plusieurs clubs. | Une panne d’instance reste limitée à un club. | Je réduis le rayon d’impact, au prix d’une supervision par instance. |
| Évolution | Je coordonnais les versions de plusieurs composants. | Je versionne une image applicative cohérente. | Je simplifie la compatibilité interne, mais je dois mettre à jour chaque instance. |
| Analyse globale | Je requêtais une base centralisée. | Je dois agréger les données de plusieurs bases. | Je rends les statistiques globales plus complexes et je dois les prévoir explicitement. |
Ce que je gagnais avec les microservices multi-tenant
Je ne veux pas réécrire l’histoire en prétendant que ma V1 n’avait aucun avantage. Avec cette approche :
- je mutualisais l’infrastructure et les ressources entre tous les clubs ;
- je centralisais les données, les sauvegardes et les statistiques ;
- je pouvais faire évoluer ou dimensionner un service indépendamment des autres, au moins en théorie ;
- je pouvais attribuer une responsabilité de déploiement claire à chaque composant ;
- je mettais à jour une plateforme partagée pour servir immédiatement tous les clubs.
Cette approche reste pertinente quand les domaines possèdent des rythmes d’évolution très différents, quand certains services absorbent une charge disproportionnée ou quand plusieurs équipes doivent déployer indépendamment. Dans mon cas, une base partagée réduisait déjà l’autonomie réelle de mes services : j’avais une architecture distribuée en surface, mais un grand point de rendez-vous au sous-sol.
Ce que me coûtaient les microservices multi-tenant
Je payais ces possibilités même lorsque je ne les utilisais pas :
- je multipliais les processus, les conteneurs, les configurations et les logs ;
- je devais gérer les erreurs réseau, la disponibilité des services et la compatibilité des contrats ;
- je conservais partout la contrainte multi-tenant et le risque d’un mauvais filtrage par club ;
- je concentrais le rayon d’impact dans une infrastructure et une base partagées ;
- je supportais le coût de SQL Server et de plusieurs services externes ;
- je ralentissais les changements qui traversaient plusieurs domaines.
Autrement dit, j’avais acheté la possibilité de scaler indépendamment cinq services, puis je passais surtout mon temps à leur demander de démarrer dans le bon ordre. C’était techniquement élégant, mais mon besoin métier ne m’envoyait pas toujours une carte de remerciement.
Ce que je gagne avec le monolithe modulaire par club
Avec la V2 :
- je déploie et je surveille un seul processus applicatif par club ;
- j’isole physiquement les données et les incidents de chaque club ;
- je remplace les appels réseau internes par des communications en mémoire ;
- je réduis le coût de la persistance grâce à SQLite ;
- je sauvegarde, restaure ou déplace plus facilement une instance ;
- je peux faire évoluer un cas d’usage complet dans une Vertical Slice ;
- je garde des modules métier identifiables sans payer le prix opérationnel des microservices.
Je gagne surtout en vitesse de développement. Une fonctionnalité traverse moins de frontières techniques et davantage de frontières utiles : celles du métier, de la validation et de l’expérience utilisateur.
Ce que je dois accepter avec le monolithe modulaire par club
Je n’obtiens pas cette simplicité gratuitement. Avec la V2 :
- je dois automatiser le déploiement, la mise à jour et la supervision de plusieurs conteneurs ;
- je dois appliquer les migrations de schéma à une flotte de bases SQLite ;
- je dois construire un mécanisme d’agrégation pour mes statistiques globales ;
- je ne peux pas dimensionner le catalogue ou la vente séparément sans extraire le module concerné ;
- je dois surveiller la concurrence d’accès, la taille des BLOB et les limites de SQLite si un club change fortement d’échelle ;
- je duplique une petite quantité de ressources pour obtenir l’isolation de chaque instance.
Je considère ces contraintes comme de meilleurs problèmes pour StockAsso aujourd’hui. Elles sont concrètes, automatisables et proportionnées à mon usage. Si un club devient un jour l’Amazon du maillot associatif, je serai ravi d’avoir un nouveau problème de montée en charge. Je prévoirai même le café.
J’organise chaque cas d’usage en Vertical Slice
À l’intérieur de mes modules, je structure le code par cas d’usage. Une tranche verticale regroupe le contrat d’une opération, son handler et sa validation. Elle traverse les couches nécessaires pour répondre à une intention métier précise.
sequenceDiagram
actor Admin as Moi, côté administration
participant UI as Page Blazor
participant Request as SaveProductRequest
participant Validator as Validateur
participant Handler as Handler Catalog
participant DB as SQLite
participant Notification as ProductChangedNotification
Admin->>UI: Je modifie un produit
UI->>Request: J’envoie la demande
Request->>Validator: Je valide les règles métier
Validator-->>Handler: La demande est valide
Handler->>DB: Je persiste le produit
DB-->>Handler: La sauvegarde réussit
Handler->>Notification: Je publie le changement
Notification-->>UI: Je rafraîchis l’état concernéJ’obtiens un chemin lisible de l’interface jusqu’à la persistance. Quand je modifie un cas d’usage, je trouve ses éléments sans traverser un musée de couches génériques. J’apprécie les abstractions, mais j’apprécie encore plus de retrouver mon code le lundi matin.
Ce que j’ai concrètement simplifié
| Fonctionnalité | Ce que j’avais en V1 | Ce que j’ai choisi en V2 | Ce que j’y gagne |
|---|---|---|---|
| Bases de données | J’utilisais un SQL Server centralisé et coûteux. | J’utilise un SQLite individuel par club. | Je ramène le coût de la base à 0 € et j’obtiens une isolation naturelle. |
| Stockage des images | Je dépendais d’Azure Blob Storage. | Je stocke les images en BLOB dans SQLite. | Je sauvegarde l’essentiel d’un club avec un seul fichier. |
| Topologie | J’orchestrais plusieurs microservices avec Aspire. | Je déploie un monolithe par club. | Je surveille un seul processus applicatif par instance. |
| Accès fournisseur | Je maintenais une console fournisseur dédiée. | Je supprime cette console et j’utilise les e-mails ou les exports. | Je réduis le nombre d’écrans et de rôles. |
| Modèle produit | Je gérais ProductGroup et des propriétés dynamiques. |
J’utilise Product avec des options directes. |
Je simplifie le schéma et je supprime les copies d’attributs. |
| Modèle financier | Je calculais des commissions arrière variables. | Je prélève 1 € pour chaque produit vendu, et non pour chaque commande. | Je rends la facturation transparente et automatique. |
| Paiements | Je supportais plusieurs moyens et intégrations. | Je rends Stripe obligatoire. | Je réduis le code d’intégration et j’automatise la commission. |
| Templates d’e-mails | Je les gérais dans Brevo. | Je les héberge localement et je les rends avec Handlebars. | Je maîtrise leur édition et je limite la dépendance au fournisseur. |
| Interfaces externes | J’exposais des API privées entre microservices. | J’expose une API REST sécurisée et un serveur MCP. | J’ouvre proprement l’application aux intégrations et aux agents IA. |
Je me suis fait accompagner par Codex, sans lui confier le volant les yeux fermés
Je me suis appuyé sur Codex pendant toutes les phases de conversion : renommage, déplacement du code, adaptation des contrats, migration des handlers, validation, formulaires Blazor, tests et cohérence générale.
Je suis un fan absolu du mode Plan. Avant de toucher au code, il me permet de poser le problème, d’explorer ses impacts et de réfléchir beaucoup plus en profondeur aux cas auxquels je ne prêterais pas immédiatement attention. C’est souvent à ce moment-là que je découvre les dépendances oubliées, les scénarios de migration un peu tordus et les fameux cas limites qui patientaient tranquillement derrière un buisson. Je gagne ainsi du temps au moment d’implémenter, mais surtout j’évite de transformer une idée simple en chasse au trésor technique.
Le fichier AGENTS.md a joué un rôle central. Il m’a permis de transformer mes décisions en contraintes explicites : organisation des projets, conventions C#, validation métier, règles de persistance, structure des pages d’administration, formulaires avec floating labels, usage des composants existants et priorité mobile.
Je n’ai pas demandé à l’IA de « moderniser le projet » en espérant que le mot moderne produise spontanément la bonne architecture. Je lui ai donné des règles vérifiables, puis j’ai avancé tranche par tranche. L’assistance devient nettement plus utile quand je remplace la télépathie par un cahier des charges.
La partie la plus longue est restée, comme toujours, le web design. Je peux déplacer une architecture entière en quelques décisions structurantes ; pour aligner parfaitement trois boutons sur un smartphone, je dois parfois convoquer Bootstrap, le CSS, les media queries et une commission de conciliation.
Ce que je retiens de cette refonte
Je ne considère pas les microservices comme une erreur universelle. Je considère qu’ils étaient devenus une réponse trop coûteuse à mon problème actuel.
Avec StockAsso2, je choisis une architecture que je peux exploiter aussi simplement que je peux la développer :
- je déploie une instance autonome par club ;
- je protège les frontières métier avec des modules ;
- j’organise les fonctionnalités en Vertical Slices ;
- je communique en mémoire avec ChannelMediator ;
- je persiste les données et les images dans SQLite ;
- je migre progressivement la V1 avec un outil dédié ;
- je conçois chaque parcours en mobile first.
Je suis passé d’une complexité distribuée à une modularité locale. J’ai moins de processus, moins de dépendances externes et moins de plomberie, sans renoncer à une organisation claire du métier.
Au fond, mon changement d’architecture tient en une phrase : je n’ai pas simplifié StockAsso parce que je voulais faire moins, je l’ai simplifié pour pouvoir faire mieux.
Et si un jour j’ai de nouveau besoin de microservices, ils seront les bienvenus. Mais cette fois, j’attendrai qu’ils résolvent un problème avant de leur offrir un conteneur.
Aucun commentaire publié pour le moment.
Ajouter un commentaire