Store — ERP SaaS
Un ERP multi-tenant qui permet aux commerçants de gérer ventes, achats, stock et abonnements — de bout en bout, sans paperasse.

Contexte
Beaucoup de commerçants gèrent encore leur activité sur des cahiers ou des feuilles de calcul : stock approximatif, marges invisibles, aucune traçabilité. L'objectif du Store était de proposer un ERP web et mobile, accessible et abordable, qui couvre tout le cycle commercial d'une boutique — du réapprovisionnement à la caisse — tout en restant simple à prendre en main.
C'est un produit SaaS : chaque entreprise dispose de son espace isolé, peut gérer plusieurs magasins et plusieurs employés aux rôles distincts, et souscrit à un abonnement. J'ai conçu et développé l'ensemble du backend ainsi que l'architecture du système.
Architecture
Le backend suit une approche Domain-Driven Design : chacun des 17 modules métier est un contexte autonome, découpé en quatre couches strictes. Cette séparation garde le cœur métier indépendant du framework et des détails d'infrastructure, et rend chaque module testable et évolutif isolément.
Isolation multi-tenant
L'isolation repose sur une stratégie applicative plutôt que sur un schéma par client. À la connexion, un jeton JWT embarque l'identité du tenant (entreprise et magasin), le rôle et les permissions. Chaque requête est ensuite systématiquement filtrée sur ce périmètre, garantissant qu'une entreprise n'accède jamais aux données d'une autre.
Le périmètre est hiérarchique : l'entreprise est le tenant racine, et chaque entreprise peut gérer plusieurs magasins. Une base PostgreSQL unique conserve toutes les données — un choix assumé qui simplifie l'exploitation et maîtrise les coûts à l'échelle visée, tout en gardant la porte ouverte à un cloisonnement plus fort si le besoin émerge.
Frontend & cohérence full-stack
Le frontend (Next.js 16 / React 19 en TypeScript) reprend la même discipline que le backend : une organisation par feature qui calque les modules métier, et une couche commune découpée en domain / application / infrastructure / presentation. Cette symétrie front/back rend le système prévisible — on retrouve les mêmes frontières d'un bout à l'autre de la stack.
Les données serveur sont gérées avec TanStack Query (cache, invalidation), l'état client avec Zustand, et les formulaires avec React Hook Form validés par Zod. L'interface s'appuie sur Tailwind CSS et shadcn/ui, est bilingue (next-intl) et installable en PWA — d'où l'accès web et mobile promis aux commerçants.
Modules clés
Ventes & caisse
Cycle de commande complet, consommation du stock en FIFO, paiements échelonnés, clients anonymes au comptoir et résumé de caisse.
Achats & fournisseurs
Commandes fournisseurs de la création à la réception, réceptions multiples, lots traçables et paiements fractionnés.
Stock & inventaire
Suivi par lot avec dates de péremption, valorisation au prix d'achat moyen, journal des mouvements et inventaires physiques avec rapport profit/perte.
Abonnements SaaS
Plans (Trial, Standard, Pro) avec limites, coupons et promotions, paiement manuel validé sur preuve (Wave, Orange Money).
Sécurité & RBAC
Authentification JWT, refresh tokens, contrôle d'accès piloté par les données : plus de 85 permissions et des rôles personnalisables par entreprise.
Reporting & audit
Tableaux de bord (top produits, marges, valorisation du stock) et journal d'audit dont la portée s'adapte au rôle de l'utilisateur.
Défis & solutions
- Défi
Modéliser des utilisateurs très différents — un ADMIN qui administre la plateforme, un propriétaire qui pilote son entreprise, des employés rattachés à ses magasins — chacun lié à des entités distinctes, sans multiplier les mécanismes d'authentification ni d'autorisation.
SolutionSéparer l'identité des accès. Une hiérarchie Person → Utilisateur (héritage JOINED) décrit qui est l'utilisateur et à quoi il est rattaché : le propriétaire porte son entreprise, l'employé son magasin. En face, une entité Account unique gère les identifiants et pointe vers un rôle porteur de permissions. Résultat : ADMIN, propriétaire et employés s'authentifient tous par le même point d'entrée et sont autorisés par le même RBAC — chaque endpoint vérifie une permission, jamais le type concret de l'utilisateur. Les rôles système restent non délégables, et le périmètre résolu (entreprise, magasin) est projeté dans le JWT pour une autorisation uniforme.
- Défi
Garantir qu'aucune entreprise ne puisse voir les données d'une autre, sur une base de données partagée.
SolutionLe tenant (entreprise + magasin) est porté par le JWT et appliqué à chaque requête via le contexte de sécurité. Le filtrage par périmètre est centralisé pour éviter toute fuite par oubli.
- Défi
Connaître à tout instant la valeur du stock et tracer chaque unité, y compris les produits périssables.
SolutionUn modèle de stock FIFO par lot (numéro de lot, date d'expiration, prix d'achat) avec journal des mouvements horodaté et détection des lots qui expirent. La valorisation s'appuie sur le prix d'achat moyen.
- Défi
Offrir des droits fins et différents selon les rôles, sans recompiler à chaque nouveau besoin.
SolutionUn RBAC piloté par les données : les permissions et les rôles vivent en base, les rôles sont personnalisables par entreprise et chaque endpoint déclare la permission requise.
- Défi
Encaisser les abonnements alors que les paiements locaux passent par Wave, Orange Money ou virement, hors application.
SolutionUn flux d'abonnement à paiement manuel : le client téléverse une preuve, un administrateur valide ou rejette, et l'activation est calculée (plan, durée, promotion, coupon). Le modèle est prêt pour une passerelle automatique le moment venu.
- Défi
Tenir la qualité sur un périmètre large et garder la base de données maîtrisée.
Solution741 tests exécutés sur une vraie base PostgreSQL, une quality gate SonarQube (couverture du code neuf ≥ 80 %), un schéma versionné par 49 migrations Flyway, le tout livré via une CI GitLab et une image Docker multistage.
Stack technique
- Java
- Spring Boot
- Spring Security
- Spring Data JPA
- JWT
- PostgreSQL
- Flyway
- TypeScript
- React
- Next.js
- Tailwind CSS
- shadcn/ui
- TanStack Query
- Zustand
- React Hook Form
- Zod
- PWA
- JUnit 5
- Mockito
- JaCoCo
- SonarQube
- Vitest
- Docker
- GitLab
- CI/CD
- Railway
- Vercel
Envie d'en savoir plus ?
Je peux vous présenter l'architecture en détail et répondre à vos questions techniques.
Me contacter