Valérian ZoltowskiApplications métier sur mesure

Étude de cas

Concevoir, déployer et exploiter une application à abonnement, seul

CasaCroqueta, un projet personnel devenu un produit complet, que je fais tourner en production.

Rôle
Conception, développement, déploiement, exploitation
Nature
Projet personnel
Statut
En ligne, en production
Paiements
Stripe en mode réel, des paiements sont passés
Voir le site en ligne

Pourquoi ce projet existe

J'avais besoin d'une application de recettes : sans publicité, avec des fonctionnalités d'IA, et une ergonomie moderne. Aucune sur le marché ne proposait ça. Je l'ai donc construite pour moi.

L'envie de la commercialiser est venue après, comme un exercice de style. Je voulais aller jusqu'au bout de la chaîne plutôt que de m'arrêter à une application qui tourne sur ma machine : des comptes, des plans payants, une facturation qui encaisse vraiment, un déploiement, des sauvegardes, et de quoi savoir ce qui se passe quand quelque chose échoue à trois heures du matin.

Disons-le franchement : ce n'est pas un succès commercial et je ne le présente pas comme tel. C'est une application complète, en ligne, dont je tiens l'exploitation. Ce que je montre ici, c'est la façon dont elle est construite.

Contraintes

Ce qui a cadré toutes les décisions

Chaque choix technique plus bas découle de l'une de ces quatre contraintes.

  • Une seule personne. Pas de relecture par un collègue, donc tout ce qui peut être vérifié automatiquement doit l'être.
  • Du temps par tranches courtes. Le projet avance par soirées. Il doit pouvoir être repris après trois semaines sans le relire en entier.
  • Un budget d'infrastructure proche de zéro. Ce qui exclut la plupart des services managés dès que l'usage monte.
  • Ça doit tourner sans surveillance. Personne n'est d'astreinte. Ce qui échoue doit soit se rattraper tout seul, soit laisser une trace exploitable.

Architecture

Comment c'est assemblé

Architecture de CasaCroquetaLe navigateur passe par Traefik, qui termine le TLS et route vers le frontend Next.js et l'API FastAPI. L'API s'appuie sur PostgreSQL et Redis, et communique avec Stripe dans les deux sens : appels sortants pour la facturation, webhooks entrants enregistrés avant traitement. Une tâche de sauvegarde chiffre et exporte la base chaque nuit.Navigateurweb et mobileTraefikTLS, routageNext.jsrendu et interfaceAPI FastAPIservices, quotasPostgreSQLdonnées, migrationsRediscache, jetons, quotasStripeabonnementsSauvegardeschiffrées, la nuitwebhooksUn serveur unique, tout en conteneurs, un seul fichier de déploiement.
Les flèches en teal sont les échanges avec Stripe. Les webhooks entrants sont enregistrés en base avant d'être traités, ce qui permet de les rejouer.

Décisions

Cinq choix, et ce que j'ai écarté

Un choix technique ne vaut que par l'alternative qu'il élimine et par ce qu'il coûte. Les voici dans les deux sens.

01

Une architecture en couches, sans dérogation possible

L'endpoint valide et appelle un service, le service porte la logique métier, le repository seul parle à la base. Aucune couche n'en saute une autre.

Pourquoi. C'est ce qui rend une application modifiable un an plus tard. Quand la règle de gestion vit à un seul endroit, on sait où aller la changer.

Écarté. La solution rapide, qui consiste à écrire la requête directement dans l'endpoint. Elle fait gagner une heure au premier écran et la reprend dix fois au bout de trente.

Ce que ça coûte. Plus de fichiers à écrire pour la même fonctionnalité, et une discipline à tenir même quand on est pressé.

02

Une source de vérité unique pour les plans et les quotas

Tout ce qui distingue un compte gratuit d'un compte payant est décrit dans un seul module. Le reste du code demande l'autorisation, il ne la déduit jamais.

Pourquoi. Un droit d'accès dispersé en conditions un peu partout finit toujours par diverger, et le jour où les deux versions ne disent plus la même chose, c'est le client qui paie et n'a pas accès.

Écarté. Vérifier le plan là où on en a besoin, au fil de l'eau. C'est plus direct à écrire et impossible à auditer ensuite.

Ce que ça coûte. Il faut penser au module central avant d'ajouter une fonctionnalité, et tenir la même liste côté interface.

03

Les webhooks Stripe enregistrés avant d'être traités

Chaque événement reçu est stocké avec son identifiant, puis traité. Un événement déjà vu n'est pas rejoué, et un traitement raté peut l'être.

Pourquoi. Stripe ne garantit ni l'unicité de la livraison ni son ordre. Sans cette table, un même paiement peut être compté deux fois, et une panne de deux minutes fait perdre des événements sans que personne ne le sache.

Écarté. Traiter l'événement directement à la réception. En développement, ça passe.

Ce que ça coûte. Une table de plus, et l'obligation de rendre chaque traitement rejouable sans effet de bord.

04

Quatre niveaux de tests, dont un contre la vraie base

Des tests unitaires sur la logique pure, des tests d'API en mémoire, des tests d'intégration en boîte noire contre un vrai PostgreSQL et un vrai Redis lancés en conteneurs, et des tests de charge sur les points les plus sollicités.

Pourquoi. Les tests unitaires ne voient ni les migrations, ni les types spécifiques à PostgreSQL, ni le comportement réel du cache. Ce sont pourtant eux qui cassent en production.

Écarté. Se contenter de tests unitaires avec des simulacres. La suite reste verte et l'application ne démarre pas.

Ce que ça coûte. Une suite plus lente, et un environnement de test à maintenir en plus du reste.

05

Un serveur unique, tout en conteneurs

Docker Compose derrière Traefik, certificats renouvelés automatiquement, sauvegardes chiffrées la nuit, déploiement par récupération d'image sans code source sur la machine.

Pourquoi. À une personne, la complexité d'exploitation se paie tous les jours. J'ai choisi ce que je saurais redémarrer sans relire de documentation.

Écarté. Une plateforme managée. Moins de choses à administrer, mais un coût mensuel qui grimpe avec l'usage et une dépendance dont on ne sort pas facilement.

Ce que ça coûte. Les mises à jour système et la surveillance sont à ma charge.

Le plus difficile

Ni le code, ni Stripe

Le point dur n'a été aucun des sujets techniques ci-dessus. Il a été de faire tenir ensemble trois choses qui ne tirent pas dans le même sens : ce que le produit promet, ce à quoi il doit ressembler, et ce qui est raisonnable à construire dans le temps disponible.

Chacune de ces trois exigences est capable de manger tout le temps du projet à elle seule. Une idée de fonctionnalité en appelle trois autres. Un écran qu'on veut plus soigné demande un composant de plus, puis un état de plus. Et une contrainte technique bien traitée peut absorber des soirées entières sans que rien de visible n'avance.

Ce que j'en retire : décider tôt ce qu'on ne fait pas, et s'y tenir. Une fonctionnalité repoussée coûte moins cher qu'une fonctionnalité construite à moitié et gardée quand même.

C'est la raison pour laquelle je travaille sur périmètre écrit. Sur CasaCroqueta, l'arbitrage se faisait un soir de semaine dans ma tête. Sur un projet payé, il est dans le devis.

Exploitation

Ce qui tourne sans moi

  • Le certificat se renouvelle seul, sans intervention.
  • La base est sauvegardée chaque nuit, et la restauration a été testée.
  • Un déploiement récupère une image déjà construite, sans code source sur le serveur.
  • Les événements de paiement laissent une trace en base, donc un échec se voit et se rejoue.
  • L'intégration continue refuse une image qui ne passe pas les tests.

Une application livrée mais non exploitable retombe sur son propriétaire au premier incident.

Recul

Ce que je referais autrement

  • Fixer le périmètre de la première version par écrit. Je l'ai gardé en tête, ce qui revient à le laisser bouger. Un périmètre écrit aurait rendu visibles les arbitrages que je faisais sans les nommer.
  • Traiter le design avant le développement, pas pendant. Reprendre l'apparence d'un écran déjà construit coûte plusieurs fois le temps de le dessiner d'abord.
  • Brancher la facturation plus tôt. C'est la partie qui touche le plus de choses. La repousser fait croire qu'elle est simple, jusqu'au moment où il faut ajuster le modèle de données pour l'accueillir.

Fiche technique

Backend
Python, FastAPI, SQLAlchemy en asynchrone, Alembic pour les migrations
Frontend
Next.js, React, TypeScript, Tailwind CSS
Données
PostgreSQL, Redis pour le cache, les jetons et les quotas
Paiement
Stripe, abonnements et webhooks
Infrastructure
Docker, Traefik, TLS automatique, sauvegardes chiffrées
Tests
Unitaires, API, intégration sur vraie base, charge
Langues
Français, anglais, espagnol

Ce que ça dit de mon travail sur vos projets

Ce que vous achetez quand vous me confiez un projet, c'est cette chaîne appliquée à votre métier : un périmètre décidé avant d'écrire du code, une application qui se modifie encore dans un an, une mise en production réelle, et de quoi savoir ce qui se passe quand ça casse.

Ou écrivez-moi directement : contact@zoltowski.fr