Développement

Architecture offline first : bâtir une application mobile résiliente face aux coupures réseau

Vous avez sans doute déjà pesté contre cette roue de chargement infinie dans le métro ou au fond d'un ascenseur. Concevoir une application qui reste fluide sans connexion n'est pas une option esthétique, c'est une nécessité technique absolue pour garantir l'adoption utilisateur. Plongeons ensemble dans les entrailles de la persistance locale.

photo de profil de Dorian
Dorian
Chef de projet IT
Offline first : concevoir une app qui fonctionne sans connexion

L'illusion de la connectivité permanente et le réveil brutal

Nous vivons dans un mensonge collectif assez confortable. Celui qui nous laisse croire que la 4G, la 5G ou le Wi-Fi sont des ressources inépuisables et omniprésentes. C'est faux. Il suffit de passer un tunnel, d'entrer dans un bâtiment aux murs épais ou de se rendre dans une zone rurale pour que cette illusion s'effondre. Pour un développeur mobile, partir du principe que le réseau est toujours là est une erreur de débutant. Une faute grave même.

L'approche classique, souvent enseignée dans les bootcamps un peu hâtifs, consiste à considérer l'application mobile comme une simple coquille vide qui va chercher son contenu sur un serveur distant via une API REST ou GraphQL. L'application est un terminal bête. Si le serveur ne répond pas, l'application ne sert à rien. Elle affiche un écran blanc, une erreur générique ou pire, elle plante. C'est frustrant pour l'utilisateur qui ne comprend pas pourquoi il ne peut pas consulter ses derniers messages déjà chargés il y a deux minutes.

Chez Dexon, nous voyons trop souvent des projets qui échouent sur cet écueil précis. L'architecture "Online First" est fragile par nature. Elle suppose un monde idéal qui n'existe pas. Passer au "Offline First", c'est changer radicalement de paradigme. Ce n'est plus "l'application appelle le serveur pour afficher des données". C'est "l'application lit sa base de données locale pour afficher des données, et tente de se synchroniser avec le serveur quand elle le peut". La nuance est gigantesque ! La source de vérité pour l'interface utilisateur devient le stockage local du téléphone, et non plus le cloud.

Cela impose une réflexion différente dès la phase de conception. On ne dessine pas les mêmes écrans. On ne prévoit pas les mêmes interactions. On doit gérer l'attente, l'incertitude et la réconciliation. C'est un défi technique passionnant mais qui demande de l'humilité face à la complexité des réseaux mobiles.

La base de données locale comme citadelle imprenable

Si l'on accepte que le réseau est un bonus et non un prérequis, il faut un endroit solide pour stocker la donnée sur le terminal. Oubliez les AsyncStorage ou les SharedPreferences pour stocker des structures complexes, ce serait du suicide en termes de performance. Il faut du lourd. Du structuré.

Le choix de la technologie de persistance est crucial. On ne peut pas se tromper ici. Si vous choisissez une solution qui ne gère pas bien les relations ou les gros volumes, vous allez droit dans le mur. Voici les options qui reviennent souvent sur la table, avec leurs forces et leurs faiblesses :

  1. SQLite : Le grand-père. Robuste, SQL standard, mais parfois lourd à implémenter sans ORM. C'est du solide, ça ne bouge pas.
  2. Realm : Très populaire, orienté objet, très rapide. Attention à la taille de l'APK qui peut gonfler.
  3. WatermelonDB : Construit sur SQLite mais optimisé pour React Native avec une approche "lazy". Très performant pour des milliers d'enregistrements.
  4. PouchDB : Parfait si vous utilisez CouchDB côté serveur, la synchro est magique. Mais peut être lent sur mobile.
  5. Core Data (iOS) / Room (Android) : Les solutions natives. Toujours une valeur sûre si on ne fait pas de cross-platform.
  6. MMKV : Pour du clé-valeur ultra-rapide, mais pas pour des données relationnelles complexes.

Le principe est toujours le même : l'UI (Interface Utilisateur) s'abonne à la base de données locale. Dès qu'une donnée change en local, l'écran se met à jour. Instantanément. Pas de loader. Pas d'attente. C'est cette réactivité qui crée l'effet "waouh" chez l'utilisateur. Il a l'impression que l'application est incroyablement rapide, alors qu'en réalité, on a juste supprimé la latence réseau de l'équation critique du rendu.

Mais attention. Stocker en local, c'est bien. Mais sécuriser cette donnée, c'est mieux. On ne peut pas laisser des données sensibles en clair dans une base SQLite accessible. Le chiffrement (via SQLCipher par exemple) devient obligatoire. C'est une contrainte de plus à gérer, des clés à sécuriser dans le Keychain ou le Keystore. C'est là que notre méthodologie de développement sécurisé prend tout son sens, car la performance ne doit jamais se faire au détriment de la confidentialité.

Le casse-tête de la synchronisation et des conflits

C'est ici que les choses se corsent vraiment. C'est ici que les cheveux tombent. Avoir des données en local, c'est facile. Faire en sorte qu'elles soient cohérentes avec le serveur et avec les autres appareils de l'utilisateur, c'est un art obscur.

Imaginez. L'utilisateur A modifie un document en mode avion. L'utilisateur B modifie le même document sur le serveur. L'utilisateur A retrouve du réseau. Que se passe-t-il ? Qui a raison ?

La stratégie naïve du "Last Write Wins" (le dernier qui a écrit écrase l'autre) est souvent utilisée par défaut. C'est une stratégie de paresseux. Elle fonctionne pour changer une photo de profil, mais pour de l'édition collaborative ou des stocks, c'est une catastrophe. Vous allez perdre des données silencieusement. Et les utilisateurs détestent ça.

Il existe des techniques plus avancées, mais elles sont complexes à mettre en œuvre :

  • Versioning des objets : Chaque modification incrémente une version. Le serveur refuse une modification si la version envoyée est inférieure à la version actuelle. L'app doit alors re-télécharger la dernière version et demander à l'utilisateur de trancher. C'est lourd pour l'UX.
  • CRDTs (Conflict-free Replicated Data Types) : Des structures de données mathématiques qui permettent de fusionner des modifications concurrentes sans conflit. C'est magique sur le papier (utilisé par des outils comme Figma ou Google Docs), mais l'implémentation est un cauchemar technique pour une application métier standard.
  • Differential Synchronization : On n'envoie que le "delta" (la différence) et non l'objet entier. Cela réduit la bande passante mais complexifie la logique backend.
  • Soft Deletes : Ne jamais supprimer une donnée réellement. Juste la marquer comme "supprimée" (deleted_at). Sinon, comment propager une suppression à un client qui était hors ligne ? Si l'objet n'existe plus sur le serveur, le client ne saura jamais qu'il doit le supprimer chez lui.
  • Queues de synchronisation : Toutes les actions locales (POST, PUT, DELETE) sont empilées dans une file d'attente persistante (Redux Offline fait ça très bien). Dès que le réseau revient, on dépile et on envoie. Mais il faut gérer les échecs, les "retries", l'ordre des requêtes... Si je crée un objet puis le modifie offline, je dois m'assurer que la création part avant la modification !

J'ai souvent des doutes sur la pertinence d'aller trop loin dans la complexité des CRDTs pour des applications simples. Parfois, il vaut mieux accepter quelques conflits rares et les gérer manuellement plutôt que de monter une usine à gaz que personne ne saura maintenir dans deux ans. C'est un équilibre précaire.

Un autre point souvent négligé est la gestion des identifiants. Si je crée un nouvel item en offline, quel est son ID ? Je ne peux pas attendre que le serveur me le donne (puisque je suis offline). Je dois utiliser des UUID (Universally Unique Identifier) générés en local. Cela change la structure de votre base de données côté serveur qui a souvent l'habitude des auto-incréments (1, 2, 3...). Il faut adapter le backend. L'Offline First impacte tout, pas juste le mobile.

Optimistic UI : mentir à l'utilisateur pour son bien

L'interface optimiste est le complément visuel indispensable de l'Offline First. Le principe est simple : on considère que l'action de l'utilisateur va réussir.

Vous cliquez sur "Like". Le cœur devient rouge immédiatement. On n'attend pas la réponse du serveur. En arrière-plan, la requête est lancée. Si elle réussit, tant mieux, on ne fait rien. Si elle échoue, on annule le changement visuel (le cœur redevient gris) et on affiche un message d'erreur discret.

Cela demande une gymnastique mentale pour le développeur. Il faut gérer deux états : l'état "désiré" et l'état "confirmé". L'interface affiche l'état désiré.

Cette approche est utilisée par toutes les grandes applications (Instagram, Twitter, Linear). Regardez nos références : les projets les plus appréciés sont ceux qui répondent au doigt et à l'œil. Cependant, il y a un piège. Si vous utilisez l'Optimistic UI pour des actions critiques, comme un virement bancaire, vous jouez avec le feu. Dire "Virement effectué" alors qu'il a échoué 2 secondes plus tard à cause d'une erreur serveur, c'est générer une panique inutile. Il faut savoir où placer le curseur. Mentir, oui, mais pas sur des sujets graves !

Parfois, je me demande si on ne va pas trop loin dans cette volonté de fluidité absolue. À force de masquer la réalité technique (le réseau est lent, le serveur est loin), on crée des utilisateurs impatients qui ne tolèrent plus la moindre friction. Est-ce vraiment rendre service à l'humain que de lui cacher la complexité du monde ? Je digresse.

La mise en place de l'Optimistic UI nécessite une gestion d'état frontend impeccable. Des outils comme React Query ou Apollo Client (pour GraphQL) intègrent ces concepts nativement. Ils permettent de "rollback" (revenir en arrière) automatiquement en cas d'erreur. C'est puissant mais ça demande une configuration fine. Une erreur de configuration et votre interface clignote ou affiche des données incohérentes.

Le défi de l'arrière-plan et de la batterie

Une application Offline First doit aussi savoir travailler quand l'utilisateur ne la regarde pas. C'est le "Background Sync". L'idée est de profiter des moments où le téléphone a du bon réseau et de la batterie pour télécharger les nouvelles données, afin que, quand l'utilisateur ouvre l'application, tout soit déjà là.

Mais là, on se heurte aux gardiens du temple : iOS et Android. Les systèmes d'exploitation mobiles sont devenus impitoyables avec les tâches de fond pour préserver l'autonomie des appareils. Vous ne pouvez pas faire ce que vous voulez.

Sur iOS, le BGAppRefreshTask est capricieux. Le système décide quand il vous donne la main. Ça peut être dans 15 minutes ou dans 6 heures. Il se base sur les habitudes de l'utilisateur. Si l'utilisateur ouvre votre app tous les matins à 8h, iOS essaiera de lancer une synchro à 7h45. C'est intelligent, mais imprévisible pour le développeur. On ne peut rien garantir.

Sur Android, WorkManager offre plus de souplesse, mais la fragmentation des constructeurs (Samsung, Xiaomi, Huawei) qui tuent les processus en arrière-plan pour économiser la batterie est un véritable enfer . Il faut se battre avec des paramètres système obscurs.

C'est là qu'on voit la différence entre une app bricolée et une app industrielle. Gérer ces contraintes demande une connaissance pointue du cycle de vie des applications. Il ne suffit pas de lancer un setInterval en JavaScript, ça ne marchera pas une fois l'écran éteint.

Tester l'imprévisible

Comment valider qu'une application fonctionne bien en mode dégradé ? Certainement pas en restant assis au bureau avec la fibre optique.

Il faut des outils pour simuler la misère. Le "Network Link Conditioner" sur Mac ou les outils de développement Chrome permettent de simuler de la 2G, de la 3G, du packet loss (perte de paquets) ou une latence élevée. C'est indispensable.

Mais le vrai test, c'est le terrain. Il faut tester les transitions :

  1. Je lance une action avec du réseau.
  2. Je coupe le réseau brutalement pendant le chargement.
  3. Je tue l'application.
  4. Je relance l'application sans réseau.
  5. Je remets le réseau.

Est-ce que la donnée est là ? Est-ce que la requête est repartie ? Est-ce que j'ai un doublon ? C'est dans ces scénarios catastrophe que l'on juge la qualité du code. Souvent, on découvre que la gestion des erreurs est trop générique. Un simple catch (e) { console.log(e) } ne suffit pas. Il faut distinguer une erreur réseau (qui doit déclencher un "retry" plus tard) d'une erreur métier (400 Bad Request) qui ne doit surtout pas être rejouée indéfiniment.

Les tests automatisés (E2E avec Detox ou Appium) doivent intégrer ces coupures réseau. C'est complexe à scripter, mais c'est le seul moyen d'éviter les régressions. Une mise à jour d'une librairie tierce peut casser tout votre mécanisme de synchronisation sans que vous ne le voyiez en test manuel standard.

Un autre aspect souvent oublié est la gestion de l'espace disque. Si on cache tout en local, la base de données grossit. Que se passe-t-il quand le téléphone est plein ? L'application crashe-t-elle ? SQLite renvoie-t-il une erreur propre ? Il faut prévoir des mécanismes de purge (Garbage Collection) pour supprimer les vieilles données dont l'utilisateur n'a plus besoin. C'est une stratégie adapté à la consommation de contenu (news, réseaux sociaux) mais plus délicate pour des données métier critiques.

Au final, l'Offline First n'est pas une "feature" que l'on ajoute à la fin du sprint. C'est une fondation. C'est comme les fondations d'une maison : si vous les avez oubliées, vous ne pourrez pas les rajouter une fois le toit posé. Il faudra tout casser.

C'est pour cela que chez Dexon, nous insistons lourdement sur cette phase d'architecture. Cela coûte plus cher au début, c'est indéniable. Mais le retour sur investissement en termes de satisfaction utilisateur et de baisse du taux de désinstallation est massif. Une application qui marche tout le temps, c'est une application en laquelle on a confiance. Et la confiance, c'est ce qui fidélise.

Parfois, je regarde certaines applications bancaires ou administratives qui tournent indéfiniment dès qu'on capte mal, et je me dis qu'il y a encore beaucoup de travail d'évangélisation à faire. Le web nous a habitués à l'instantanéité connectée, mais le mobile est un animal différent, sauvage, instable. Le dompter demande de la technique, mais surtout une bonne dose de pragmatisme et d'empathie pour cet utilisateur coincé dans le tunnel du RER B qui veut juste lire son article.

L'approche offline first demande un investissement initial conséquent et une rigueur architecturale sans faille. C'est le prix à payer pour offrir une expérience utilisateur ininterrompue et robuste. Ne laissez plus vos utilisateurs dépendre de la qualité aléatoire de leur réseau mobile, reprenez le contrôle de vos données localement.

Nos derniers articles

Explorez l'univers digital à travers nos articles captivants, abordant les dernières tendances et astuces du domaine numérique.

Société qui développe des applications iOS et Android

L'art complexe de bâtir des écosystèmes mobiles performants sur iOS et Android

Martin - Ingénieur / Développeur
Quelle est la meilleure agence pour créer une application mobile ?

Dénicher le partenaire stratégique idéal pour le développement de votre application mobile

Baptiste - Co-Founder / CEO
Sélectionner le partenaire technique idéal pour votre application mobile : au-delà des promesses commerciales

Sélectionner le partenaire technique idéal pour votre application mobile : au-delà des promesses commerciales

Baptiste - Co-Founder / CEO
Offline first : concevoir une app qui fonctionne sans connexion

Architecture offline first : bâtir une application mobile résiliente face aux coupures réseau

Dorian - Chef de projet IT

Confiez votre projet à nos
experts en applications

Notre équipe pluridisciplinaire de designers, développeurs et coachs apporte à votre solution une véritable plus-value à court, moyen et long terme grâce à une maîtrise parfaite de son architecture globale.

Développeurs, designers, chefs de projet, travaillant au sein des bureaux de l'agence Dexon spécialisée en création d'applications mobiles et webDéveloppeurs, designers, chefs de projet, travaillant au sein des bureaux de l'agence Dexon spécialisée en création d'applications mobiles et webDéveloppeurs, designers, chefs de projet, travaillant au sein des bureaux de l'agence Dexon spécialisée en création d'applications mobiles et webDéveloppeurs, designers, chefs de projet, travaillant au sein des bureaux de l'agence Dexon spécialisée en création d'applications mobiles et webDéveloppeurs, designers, chefs de projet, travaillant au sein des bureaux de l'agence Dexon spécialisée en création d'applications mobiles et webDéveloppeurs, designers, chefs de projet, travaillant au sein des bureaux de l'agence Dexon spécialisée en création d'applications mobiles et webDéveloppeurs, designers, chefs de projet, travaillant au sein des bureaux de l'agence Dexon spécialisée en création d'applications mobiles et webDéveloppeurs, designers, chefs de projet, travaillant au sein des bureaux de l'agence Dexon spécialisée en création d'applications mobiles et webDéveloppeurs, designers, chefs de projet, travaillant au sein des bureaux de l'agence Dexon spécialisée en création d'applications mobiles et web

Ils parlent de nous

Découvrez ce que la presse dit de nous ! Nous sommes fiers de partager les mentions et analyses qui mettent en lumière notre travail et nos innovations.

logo BFM Businesslogo Le Figarologo Challengeslogo la Tribunelogo CNEWS

Un projet à nous soumettre ?

Étape 2/2
01 87 66 10 43

Paris • Lyon • Marseille • Nice • Genève

logo CII

Agrément CII

Votre entreprise peut prétendre à un crédit d'impôt équivalant à 20% des coûts liés au développement de sa solution.