Architecture Flutter / Firebase — 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 packages dont les dépendances sont vérifiées par le compilateur Dart. 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 packages, 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 Firestore se glisse dans un widget.

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 packages.

  • Une feature = une tranche verticale dans 4 packages séparés
  • Le découpage en packages 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 Firebase, sans widget
  • Les repos sont des interfaces — on branche un fake en une ligne de DI
  • Changer Firestore pour autre chose 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

Clean Architecture 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 : sealed class Failure + switch exhaustif — ajouter un cas d'échec devient une erreur de compilation partout où il manque
  • Either sur les writes : le chemin d'échec est dans la signature, pas dans un try/catch oublié
  • Tout SDK externe est wrappé 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'appelle aucune fonction backend : les callables sont bannies. Il écrit un document status: pending, un trigger le traite. Une seule surface d'entrée à gouverner au lieu d'un parc d'endpoints HTTP
  • Tout write passe par un usecase obligatoire — un seul endroit où valider, pas douze chemins d'écriture
  • Les secrets backend vivent dans Secret Manager, jamais dans un .env en prod. Côté client, envied les injecte à la compilation
  • Aucune exception Firebase brute ne remonte à l'UI : elle est traduite en Failure typée à la frontière data

La règle

Ce que le compilateur refuse

Chaque package 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 pubspec.yaml. Une case rouge, c'est un build cassé.

Ce que le compilateur refuse
Le package… core domain data presentation app
core ·
domain ·
data ·
presentation ·
app ·
  • peut importer
  • ne compile pas
// ✅ OK — presentation → domain
import 'package:myapp_domain/auth/entities/app_user.dart';

// ❌ COMPILATION ERROR — data qui fuite dans presentation
import 'package:myapp_data/auth/datasources/auth_remote_datasource.dart';

L'outillage

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

Le reste passe par `custom_lint`. Ce sont des règles qu'on n'a plus à défendre en revue de code — elles échouent toutes seules.

  • molecule_no_riverpod erreur
    Une molecule reçoit tout par son constructeur. C'est ce qui la rend testable sans un seul mock.
  • connector_must_be_consumer erreur
    Le connector est le seul point où Riverpod touche l'arbre de widgets. Ailleurs, c'est une fuite.
  • max_function_lines · 50 avertissement
    Une fonction plus longue cache une décision qu'on n'a pas nommée.
  • max_class_lines · 200 avertissement
    Au-delà, la classe fait deux choses. On la coupe avant qu'elle durcisse.
  • one_public_class_per_file 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 // ignore_for_file 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
Widget trop gros Personne ne le voit Lint error à 200 lignes
Molecule avec Riverpod Ça marche Lint error
Test d'un widget 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 : un datasource importé dans un widget, trois classes dans un fichier, un setState « 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.

Je n'ai 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 Melos + packages 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 Firestore, 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 9 vues du dev-kit : packages, couches, flux de données, atomic design, injection Riverpod, erreurs, nommage, UI, services. Navigation au clavier (1-9, Échap), ou Auto Tour.

Ouvrir en plein écran ↗

Tu as une app qui commence à peser

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