Retour d’expérience technique – Decembre 2025
1. Contexte et origine du projet
Il y a quatre mois, un arbitre de volley-ball actif en ligue régionale Centre-Val de Loire m’a partagé un constat très répandu dans les clubs amateurs :
- Notation des matchs réalisée sur papier ou tableau effaçable
- Transmission des feuilles par photo ou ressaisie manuelle dans un tableur
- Suivi des licences souvent approximatif (fichiers partagés + relances manuelles)
- Absence de traçabilité fiable des évaluations individuelles des joueurs
L’objectif n’était pas de concevoir une solution nationale ou à vocation commerciale, mais de créer un outil réellement adopté par les acteurs du terrain : arbitres, entraîneurs, joueurs et responsables administratifs de club.

2. Fonctionnalités principales livrées (MVP – 2 mois)
- Inscription et connexion avec sélection de rôle (joueur – arbitre – entraîneur – administrateur de club)
- Consultation du statut de licence (valide / expirée / en attente) + alerte avant échéance
- Notation en temps réel pendant le match (scores + évaluation qualitative du joueur)
- Double grille d’évaluation : performance technique + comportement sportif
- Consultation sécurisée et filtrée des statistiques (selon le rôle de l’utilisateur)
- Suppression autonome et complète du compte utilisateur (exigence RGPD et App Store)
3. Architecture retenue
Type : application mobile cross-platform + backend API REST
| Couche | Technologie choisie | Justification principale |
|---|---|---|
| Frontend mobile | React Native + Expo SDK | Développement rapide iOS/Android, OTA updates, build simplifié |
| Backend | Node js + Prisma | Flexibilité, typage fort, OpenAPI automatique |
| Base de données | PostgreSQL 16 | Contraintes d’intégrité, JSONB, performances solides |
| Authentification | Firebase Authentication | Gestion déléguée des identités, JWT natif, support MFA |
| Notifications | Expo notifications | Fiabilité cross-platform, intégration native avec Auth |
| Hébergement | Google Kubernetes Engine (GKE) | Scalabilité horizontale, gestion automatisée, haute disponibilité |
L’architecture sépare clairement :
- la logique métier
- le contrôle d’accès basé sur les rôles (RBAC)
- la persistance des données
Les configurations sensibles (clés, credentials, variables d’environnement) sont gérées exclusivement via les mécanismes natifs de Google Cloud et Firebase, jamais codées en dur ni exposées dans les dépôts.
Diagramme d’architecture globale
graph TD
A[Mobile - React Native + Expo] -->|HTTPS + JWT Firebase| B[API FastAPI]
B -->|Vérification JWT + RBAC| C[PostgreSQL]
subgraph Backend
B
D[Middleware RBAC]
E[Service Notation]
F[Service Licences]
B --> D
D --> E
D --> F
end
subgraph Database
C --> G[Users + Roles]
C --> H[Matches]
C --> I[Ratings - perf + behavior]
C --> J[Licenses]
end
K[FCM - Notifications] -->|push| B
B -->|push| K
L[Club Admin] -->|licence management| B
B -->|licence management| L
M[GKE - Kubernetes] -->|Orchestration| B
N[Firebase Auth] -->|Authentication| A
A -->|Authentication| N
4. Points techniques clés traités
4.1 Modèle relationnel (extrait simplifié et anonymisé)
Le schéma est conçu pour garantir l’intégrité référentielle et permettre une évolution future sans refactor majeur.
-- Tables principales (extrait anonymisé)
CREATE TABLE users (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
email text UNIQUE NOT NULL,
role text NOT NULL CHECK (role IN ('joueur','arbitre','entraineur','administrateur')),
club_id uuid,
created_at timestamptz DEFAULT now()
);
CREATE TABLE licenses (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid REFERENCES users(id) ON DELETE CASCADE,
status text NOT NULL,
expires_at date NOT NULL
);
CREATE TABLE matches (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
date timestamptz NOT NULL,
club_id uuid
);
CREATE TABLE ratings (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
match_id uuid REFERENCES matches(id),
player_id uuid REFERENCES users(id),
score_technique integer CHECK (score_technique BETWEEN 1 AND 5),
score_comportement integer CHECK (score_comportement BETWEEN 1 AND 5),
created_by uuid REFERENCES users(id)
);
4.2 Contrôle d’accès basé sur les rôles (extrait conceptuel)
// middleware/requireRole.js
const requireRole = (...allowedRoles) => {
return (req, res, next) => {
const user = req.user
if (!user || !user.role) {
return res.status(401).json({ error: 'Non authentifié' })
}
if (!allowedRoles.includes(user.role)) {
return res.status(403).json({ error: 'Accès interdit pour ce rôle' })
}
next()
}
}
module.exports = requireRole
5. Principaux défis rencontrés et solutions mises en place
- Saisie concurrente sur un même match → verrou léger + contrôle de version
- Friction UX pour les arbitres en match → maximum 3 écrans, feedback visuel immédiat
- Gestion fine des permissions → RBAC centralisé + tests exhaustifs
- Fiabilité des notifications push → combinaison FCM + fallback email
- Conformité RGPD / App Review → flow de suppression autonome + anonymisation
6. Enseignements tirés (point de vue ingénieur)
- Une mauvaise modélisation relationnelle coûte beaucoup plus cher à corriger qu’à faire correctement dès le départ
- La gestion des rôles semble simple → elle devient rapidement le composant le plus complexe
- Un arbitre en plein match tolère zéro latence et zéro friction
- Les notifications push cross-platform restent un point faible récurrent
- Penser traçabilité et audit dès le début évite des crises de confiance
7. État actuel & perspectives
Statut : version 1.0 soumise à l’App Store (décembre 2025)
Clubs pilotes : 21 clubs en Centre-Val de Loire (~25 utilisateurs actifs)
Prochaines étapes envisagées :
- Export PDF / CSV des rapports de match
- Support multi-clubs pour les comités départementaux
- Synchronisation temps réel collaborative (optionnel via Firebase ou PostgreSQL LISTEN/NOTIFY)
Je vous remercie pour votre lecture.
