Fellwin Studio
Le logiciel de caisse conçu pour la bijouterie sénégalaise.
18 mois
en production continue
0
incident bloquant
100 %
auto-hébergé · OVH
En bref
- Le problème
- Les bijouteries sénégalaises n'avaient le choix qu'entre un logiciel de caisse occidental générique et un fichier Excel, dont aucun ne modélisait le dépôt, le rachat d'or, le coût façon ni les paiements étalés.
- La décision
- Construire un logiciel de caisse multi-locataire qui épouse ces opérations, avec les invariants financiers tenus par la structure du code et l'isolation entre bijouteries rejouée automatiquement à chaque intégration.
- Le résultat
- Le système tourne en continu depuis sa mise en production, sans incident bloquant, comme outil quotidien de l'ouverture de caisse le matin à la réconciliation du soir.
Le contexte
Sunu Bijou est le premier système jamais livré sous le nom Fellwin pour un client sénégalais. C'est un POS multi-tenant pour les bijouteries de Dakar et plus largement du Sénégal : un secteur structurant de l'économie locale, mais sur lequel il n'existait pas, au moment où nous avons démarré, de logiciel pensé pour la manière dont il fonctionne réellement.
Ni les logiciels de caisse occidentaux ni les outils maison sur Excel ne modélisaient les opérations clés du métier : le dépôt, le rachat d'or, les carats, le coût façon, les paiements étalés.
Les solutions existantes étaient soit des POS occidentaux génériques adaptés à la marge, soit des outils maison construits sur Excel. Ni l'un ni l'autre ne modélisait correctement les opérations clés du métier : le dépôt (avance versée par le client sur une commande personnalisée), le rachat (achat d'or au client au prix du jour), la distinction entre or 18 carats et 21 carats, le coût façon (le travail de l'artisan, ajouté au prix de la matière), les paiements partiels étalés.
Le système devait épouser ces réalités, pas les forcer dans une forme étrangère.
Les décisions d'architecture
Le socle est NestJS sur PostgreSQL, pour que les invariants du métier soient tenus par la structure du code plutôt que dispersés dans des conditions ad hoc.
NestJS sur PostgreSQL, pas Express + Postgres directement. Le métier comporte plusieurs sous-domaines (vente, dépôt, rachat, caisse, multi-locataire) qui partagent des invariants forts : un dépôt ne peut pas devenir négatif, un rachat doit être tracé, une caisse doit s'équilibrer en fin de journée. NestJS apporte la structure modulaire et le système de pipes/guards qui rendent ces invariants explicites au lieu de les dissimuler dans des conditions ad hoc.
Trade-off assumé : NestJS impose une courbe d'apprentissage non triviale et un coût de boilerplate par module. Le coût se paie une fois ; les invariants restent vrais pour toujours.
L'accès aux données passe par MikroORM plutôt que Prisma, parce qu'une vente touche plusieurs entités et doit s'écrire entièrement ou pas du tout.
MikroORM en mode Unit-of-Work, pas Prisma. Une vente Sunu Bijou implique souvent plusieurs entités modifiées atomiquement : créer un mouvement de caisse, débiter le stock, mettre à jour le solde de dépôt du client, enregistrer la transaction. MikroORM offre nativement le pattern Unit of Work : on modifie les entités librement dans la requête, et un seul `flush()` à la fin écrit tout ou rien. Sur du code financier, c'est moins de bugs de cohérence que la pose manuelle de transactions Prisma.
Trade-off assumé : MikroORM a une communauté plus petite et un rythme de release plus lent que Prisma. Nous avons contribué quelques cas de tests upstream quand un edge case nous gênait ; c'est le prix accepté pour la cohérence transactionnelle.
Toutes les bijouteries partagent une seule base, séparées par une colonne de locataire dont l'étanchéité est rejouée par des tests à chaque intégration.
Multi-tenant single-database avec discrimination par colonne tenant_id, pas Schema-per-tenant PostgreSQL. Le profil d'usage de Sunu Bijou est multi-bijouteries indépendantes, avec des volumes individuels modestes. Le single-database simplifie radicalement l'exploitation : une seule migration à appliquer, un seul backup à vérifier, un seul plan de monitoring. Toutes les requêtes passent par un middleware Row-Level Security qui injecte le tenant_id à partir du JWT : le code applicatif n'a jamais à y penser.
Trade-off assumé : L'isolation entre locataires repose sur une couche unique : c'est le point à tenir, et nous le traitons comme un invariant de sécurité plutôt que comme un détail d'implémentation. Des tests d'intégration dédiés rejouent l'accès croisé entre locataires à chaque CI. Le schema-per-tenant ne supprime pas ce coût, il le déplace vers l'exploitation.
L'exécution
Cinq minutes du commit à la productionTout est conteneurisé et déployé automatiquement sur nos propres serveurs : cinq minutes du commit à la production, et des sauvegardes vérifiées par restauration test chaque trimestre.
Sunu Bijou est conteneurisé avec Docker (Dockerfile multi-stage,
exécution non-root) et déployé sur OVH (deux VPS dédiés Fellwin, un
pour la base de données, un pour l'application). Le reverse-proxy
nginx termine TLS via Let's Encrypt (renouvellement automatisé). Les
sauvegardes PostgreSQL quotidiennes sont vérifiées par restauration
test une fois par trimestre. CI/CD passe par GitHub Actions : chaque
push sur main déclenche les tests Vitest, le build Docker, le push
de l'image vers GHCR, puis le déploiement par SSH avec
docker compose up --force-recreate. Le pipeline tient en cinq
minutes du commit à la production.
Les bijouteries qui utilisent Sunu Bijou aujourd'hui voient le système comme un outil quotidien : ouverture de caisse le matin, ventes à la caisse pendant la journée, fermeture-réconciliation le soir. Il fait ce qu'il fait, sans demander d'attention particulière. C'est exactement ce qu'on attend d'un logiciel d'exploitation.
Les leçons
Leçon 1La difficulté n'était pas le code mais la modélisation : deux formes de dépôt que nous n'avions pas prévues ont imposé de repenser le module.
La leçon principale de Sunu Bijou ne porte pas sur le code mais sur la modélisation. Les premières versions du module de dépôt traitaient le dépôt comme un solde positif simple : une somme à reporter sur les ventes suivantes. Cela suffisait pour 80 % des cas. Mais les bijouteries que nous accompagnions nous ont rapidement signalé deux configurations que notre modèle ne couvrait pas : le dépôt à plusieurs déposants (la mère et la fille déposent ensemble pour une commande commune) et le dépôt avec remboursement partiel en or (le client annule une partie de sa commande et récupère la moitié en gramme d'or). Repenser le module a pris une semaine. Ces deux configurations couvrent 5 % des transactions mais 100 % de la confiance du client.
Leçon 2Le test de restauration n'a pas seulement validé la sauvegarde : il a révélé un bug dormant sur la chronologie des prix de l'or.
La seconde leçon porte sur les sauvegardes. Nous avons testé la procédure de restauration trois mois après la première mise en production, en condition de panique simulée. La restauration a fonctionné ; mais nous avons trouvé en l'examinant un bug de chronologie sur la table des prix de l'or (un index manquant pouvait faire renvoyer le prix de la veille pour une vente du jour). Le bug était dormant ; sans le test de restauration, il aurait fini par se manifester en production un samedi à l'ouverture.
En production aujourd'hui
Sunu Bijou est en exploitation continue depuis 18 mois sur les bijouteries qui l'ont adopté. Aucun incident bloquant à ce jour. La roadmap 2026 ajoute un module de gestion d'atelier (suivi des commandes personnalisées de la prise de mesure à la livraison) et une interface mobile pour les déplacements en clientèle.
Le produit a sa propre vitrine : sunubijou.sn.
Un système à concevoir, déployer ou reprendre en main ?
Avec ou sans cahier des charges : décrivez votre situation, nous répondons par écrit sous 48 h ouvrées.