Architecture d'app — le layou-dev-kit

Un mauvais import ne compile pas.

Une architecture qui tient sur de la bonne volonté ne tient pas. Celle-ci est découpée en modules dont les dépendances sont vérifiées par le compilateur. Si presentation importe data, ce n'est pas un warning, ni un lint, ni une remarque en revue : c'est une erreur rouge qui bloque le build.

Les 5 modules, et ce que chacun a le droit de voir

Le coût du sans-archi

Ce qui casse une app, ce n'est presque jamais la techno

Le refactor à 18 mois

L'app marche, elle grossit, et chaque nouvelle feature coûte plus cher que la précédente. Le moment où il faudrait accélérer est celui où on réécrit.

Le dev qui ne peut pas partir

Une seule personne sait pourquoi ce fichier est là. La connaissance est dans une tête, pas dans la structure.

La review qui parle de style

On discute d'indentation et de nommage pendant qu'un appel à la base se glisse dans un composant.

Les fondations

SOLID, KISS, YAGNI — et ce qui les applique

Tout le monde cite ces principes. La question n'a jamais été de les connaître : c'est de savoir ce qui les fait tenir un mardi soir, sur une feature en retard, quand personne n'a le temps de relire. Pour chacun, voilà le mécanisme qui l'applique sans dépendre de la bonne volonté de l'équipe.

Single Responsibility

Un fichier, une raison de changer.

Une seule classe publique par fichier, 50 lignes max par fonction, 200 par classe. Au-delà, le lint casse le build : on ne discute pas en revue, on découpe.

Open / Closed

Ouvert à l'extension, fermé à la modification.

Une seconde implémentation s'ajoute à côté de la première — SqlUserRepository à côté de RestUserRepository — sans renommer ni toucher un seul appelant. Le nom porte la techno, pas le fait d'implémenter.

Liskov Substitution

Une implémentation doit pouvoir en remplacer une autre sans surprise.

Chaque interface a son Fake…, injecté à la place de la vraie en test. Un seul préfixe de test est autorisé : pas de Mock, Stub et InMemory côte à côte qui laissent croire à trois contrats différents.

Interface Segregation

Un contrat par rôle métier, pas un contrat fourre-tout.

Une interface par feature — AuthRepository, OrderRepository — déclarée dans domain. Et les lectures ne traversent pas un cas d'usage : on ne fabrique pas une couche pour le plaisir de la symétrie.

Dependency Inversion

Le métier ne dépend de rien. Tout le reste dépend du métier.

C'est la matrice plus bas. domain déclare l'interface et n'importe personne, data l'implémente, app est le seul endroit où les deux se rencontrent. Une flèche à l'envers ne compile pas.

Keep It Simple

La solution la plus simple qui tient.

80 % des services font cinq lignes : une classe, une injection. La cérémonie complète — interface, fake, erreurs typées — est réservée aux 20 % où elle paye : paiement, authentification, ce qui se teste en isolation.

You Aren't Gonna Need It

On n'écrit pas pour un besoin qu'on n'a pas.

Une interface ne s'extrait pas « au cas où » : elle s'extrait le jour où un test a besoin d'un fake. Pré-abstraire, c'est prendre de la dette pour un futur qui n'arrive pas.

Separation of Concerns

Chaque couche a un seul sujet.

Un composant feuille ne connaît que ses paramètres : zéro accès au gestionnaire d'état, vérifié par le lint. Un seul point de l'arbre d'UI branche l'état — ailleurs, c'est une fuite.

Composition > héritage

On assemble des petites pièces, on n'empile pas des classes.

Plusieurs petits états qui s'observent, plutôt qu'un objet-dieu de 800 lignes. Même règle côté UI : un composant qui en contient un autre est un organisme, pas une feuille.

Un principe manque volontairement à cette liste : DRY. Il s'arrête à la frontière data, où le DTO duplique la forme de l'entité — exprès. C'est ce doublon qui fait qu'un champ renommé côté base casse un mapper, et pas quinze écrans.

Ce que ça rapporte

Quatre bénéfices, et le mécanisme derrière chacun

Une promesse d'architecture sans mécanisme, c'est du slogan. Pour chacune, voilà ce qui l'applique concrètement.

Scalabilité

Ajouter du monde sans ralentir l'équipe

Le monorepo est découpé par couche, pas par écran. Deux devs sur deux features ne se marchent pas dessus : ils ne touchent pas les mêmes modules.

  • Une feature = une tranche verticale dans 4 modules séparés
  • Le découpage en modules rend le build incrémental : on ne recompile pas l'app entière
  • Un nouveau dev lit 5 dossiers, pas 300 fichiers — la structure lui dit où va son code
  • Les conventions de nommage sont fixes : le fichier à créer se déduit, il ne se négocie pas

Maintenabilité

Le rayon d'impact d'un changement est connu d'avance

Quand une règle métier bouge, elle bouge dans domain. Rien d'autre ne compile différemment. C'est ça, une archi tenue : savoir ce qu'on ne casse pas.

  • domain est pur : testable sans émulateur, sans base de données, sans composant d'UI
  • Les repos sont des interfaces — on branche un fake en une ligne d'injection
  • Changer de base de données ne touche que data
  • Max 50 lignes par fonction, 200 par classe, 1 classe publique par fichier

Évolutivité

Le MVP et la V3 sont le même code

Les couches sont posées dès le jour 1, même pour un MVP. Pas de code jetable, donc pas de réécriture au moment où l'app commence enfin à marcher.

  • Les erreurs sont typées : un type d'erreur fermé + un filtrage exhaustif — ajouter un cas d'échec devient une erreur de compilation partout où il manque
  • Le type de retour porte l'échec (Either / Result) : le chemin d'erreur est dans la signature, pas dans un try/catch oublié
  • Tout SDK externe est encapsulé avant d'atteindre le code de l'app
  • Les migrations se font couche par couche, pas en big bang

Sécurité

Peu de points de passage, donc peu de choses à auditer

Une architecture ne sécurise pas une app — elle décide du nombre d'endroits par lesquels il faut passer. C'est ce nombre qui rend un audit faisable.

  • Le client n'ouvre pas un endpoint par besoin. Il écrit une intention en base, un déclencheur serveur la traite. Une seule surface d'entrée à gouverner au lieu d'un parc d'endpoints
  • Tout write passe par un cas d'usage obligatoire — un seul endroit où valider, pas douze chemins d'écriture
  • Les secrets serveur vivent dans un gestionnaire de secrets, jamais dans un .env de production. Côté client, ils sont injectés à la compilation
  • Aucune exception brute du SDK ne remonte à l'UI : elle est traduite en erreur métier typée à la frontière data

La règle

Ce que le compilateur refuse

Chaque module déclare ses dépendances. Ce tableau n'est pas une convention d'équipe affichée sur un mur : c'est l'état réel des manifestes. Une case rouge, c'est un build cassé.

Ce que le compilateur refuse
Le module… core domain data presentation app
core ·
domain ·
data ·
presentation ·
app ·
  • peut importer
  • ne compile pas
// ✅ OK — presentation → domain
import  app/domain/auth/entities/app_user

// ❌ ERREUR DE COMPILATION — data qui fuite dans presentation
import  app/data/auth/datasources/auth_remote_datasource

L'outillage

La discipline, déléguée à la machine

Ce que le compilateur ne peut pas voir passe par des règles de lint maison. Ce sont des règles qu'on n'a plus à défendre en revue de code — elles échouent toutes seules.

  • Un composant feuille ne touche pas le gestionnaire d'état erreur
    Un composant feuille reçoit tout par ses paramètres. C'est ce qui le rend testable sans un seul mock.
  • Un seul point de branchement entre l'état et l'arbre d'UI erreur
    Le connecteur est le seul endroit où l'état touche l'arbre de composants. Ailleurs, c'est une fuite.
  • 50 lignes max par fonction avertissement
    Une fonction plus longue cache une décision qu'on n'a pas nommée.
  • 200 lignes max par classe avertissement
    Au-delà, la classe fait deux choses. On la coupe avant qu'elle durcisse.
  • 1 classe publique par fichier avertissement
    Le chemin du fichier devient l'index du projet — on trouve sans chercher.

Et il n'y a pas d'échappatoire : désactiver une règle avec un commentaire ignore est interdit par le kit. On refactorise.

Avant / après

Le même raccourci, dans les deux mondes

Sans le dev-kit Avec
Import interdit Ça compile (mais c'est cassé) Erreur de compilation
Composant trop gros Personne ne le voit Lint error à 200 lignes
Composant feuille branché sur l'état Ça marche Lint error
Test d'un composant Mock hell Zéro mock
Nouvel agent IA Doit relire toutes les conventions Le compilateur le guide

Pourquoi ça compte en 2026

Une archi contraignante, c'est une archi qu'un agent sait suivre

Un agent IA écrit vite, et il triche : une source de données importée dans un composant, trois classes dans un fichier, un état local « parce que c'est plus simple ». Le pire, c'est que ça compile — et que ça pourrit le codebase en silence. Ce qui l'arrête n'est pas un prompt plus long : c'est un projet où le raccourci ne compile pas. L'agent reçoit l'erreur de build, comprend la contrainte, et se corrige sans intervention. Le compilateur est le relecteur qui ne dort jamais et ne perd pas son contexte.

Nous n'avons pas besoin de faire confiance à l'IA. L'architecture elle-même est le garde-fou.
Voir la boucle de livraison complète →

Honnêtement

Ce que ça coûte, et ce que ça ne fait pas

Ce n'est pas gratuit

Compter 2 à 3 jours de scaffolding du monorepo et des modules sur le premier projet, et environ une journée pour écrire les règles de lint avec leurs tests. Ensuite c'est réutilisable d'un projet à l'autre.

Sur un MVP jetable, c'est overkill

Un hackathon, un prototype qu'on jette dans deux semaines : les 4 couches coûtent plus qu'elles ne rapportent. Ça devient rentable quand le code doit survivre à son auteur.

Ce n'est pas un cadre de sécurité

Le dev-kit ne contient ni règles d'accès à la base, ni politique de chiffrement, ni conformité RGPD. Il réduit le nombre de points de passage — l'audit et les règles restent un travail à part entière.

En détail

La visualisation interactive

Les mêmes 9 vues — modules, couches, flux de données, atomic design, injection, erreurs, nommage, UI, services — déclinées sur trois stacks : Flutter/Firebase, React Native/Supabase, TanStack/Convex. Les onglets en haut changent de stack, l'architecture ne change pas. Navigation au clavier (1-9, Échap), ou Auto Tour.

Ouvrir en plein écran ↗

Votre app commence à peser

Reprise d'un existant ou démarrage from-scratch : nous regardons ce que coûterait de tenir cette archi chez vous, et ce que ça vous éviterait.