Stack technique
Ce que le produit utilise, et pourquoi. Les décisions viennent de DEC-01 et DEC-08 (prises le 28 octobre 2026) dans Plane, où vit le backlog. Le dossier vision ne couvre pas ces choix.
Services
- API : C#, .NET 10 et ASP.NET Core, dans sa propre image Docker. Un seul projet, deux groupes de routes.
/v1est l'API publique pour les tiers, servie surapi.<domaine>./bffsert le site et l'app mobile, sousapp.<domaine>/bff, donc sur la même origine que le site. - Base : PostgreSQL 19 seulement, dans sa propre image. On tourne sur
19rc1en attendant la version finale. - Site web : application statique avec Vite, React, TanStack Router et Query, Tailwind 4 et shadcn. Elle sert aussi de web public : le dossier veut des profils, des playlists et des fiches consultables sans compte depuis un lien partagé.
- App mobile : la même application, enveloppée dans Capacitor, pour iOS et Android dès le lancement. Un seul code pour les trois. React Native a été écarté : il aurait fallu maintenir un deuxième front à côté du web public.
- Authentification : Keycloak, auto-hébergé dans le cluster (TECH-05). Licence Apache 2.0 sans édition payante : aucune limite sur les utilisateurs ni sur les fournisseurs de connexion sociale, et on peut l'étendre (SPI Java) et thématiser ses pages en React avec Keycloakify. Zitadel a été écarté : ses offres payantes limitent la version gratuite. Le web se connecte par le BFF, qui pose un cookie de session sur
app.<domaine>. L'app mobile passe aussi par le BFF, pas par l'API publique : elle obtient son jeton de Keycloak (code d'autorisation avec PKCE, retour dans l'app par un lien profond) et l'envoie au BFF, qui le valide. Pas de cookie sur mobile, la WebView bloque les cookies entre origines. Le BFF doit donc rester compatible avec les versions de l'app encore installées. Connexion par Google et Apple seulement, sans mot de passe (décision du 29 octobre 2026, qui remplace « courriel et mot de passe au lancement ») : pas de SMTP à gérer, et aucun mot de passe de vraie personne stocké chez nous. Seuls les deux comptes des tests de vérification avant une vague en ont un. Apple est obligatoire sur iOS dès qu'on offre une connexion sociale (règle 4.8 de l'App Store); Keycloak le prend en charge par l'extensionklausbetz/apple-identity-provider-keycloak, ajoutée à notre image. Un compte Google ou Apple est lié automatiquement à un compte existant qui a le même courriel, puisque ces deux fournisseurs vérifient le courriel. - Spotify, mis de côté (29 octobre 2026). Essayé comme fournisseur de connexion, puis désactivé : en mode développement, Spotify exige un abonnement Premium du propriétaire de l'app (sinon
/v1/merépond 403) et n'accepte que 5 utilisateurs, inscrits à la main dans son tableau de bord. Le mode étendu n'est pas ouvert à un projet de notre taille. Sa config reste dansrealm.yaml(fournisseur « OAuth v2 » générique de Keycloak) et se réactive en fournissant ses identifiants. Spotify ne garantit pas le courriel : la liaison à un compte existant demande de se reconnecter avec le fournisseur déjà lié. - Documentation : ce site, avec Docusaurus.
Un nginx répartit le trafic selon le sous-domaine : docs. pour la doc, app. pour l'application (app./bff/* va à l'API), et api., qui n'expose que /v1. En local, c'est le service edge de compose.yaml. Sur Kubernetes, un Ingress applique les mêmes règles.
Dépendances de l'app
@tanstack/react-virtual(31 octobre 2026, US-15) : le journal défile sans pages et peut compter des milliers d'écoutes. Seules les lignes visibles restent dans la page. De la même famille que le routeur et Query, sans dépendance de plus.
Dépendances de l'app mobile
Rangées dans src/capacitor, avec le code qui ne sert qu'au mobile. Acceptées par Samuel le 31 octobre 2026, pour la connexion (TECH-05) :
@capacitor/appet@capacitor/browser, plugins officiels de Capacitor : le premier reçoit le retourmusiqud://callback, le second ouvre Keycloak dans le navigateur du système (SFSafariViewController sur iOS, Custom Tabs sur Android). Le PKCE et l'échange des jetons sont à nous, environ 150 lignes. Écartés :@capacitor-community/generic-oauth2, qui n'existe pas encore pour Capacitor 8, et@capgo/capacitor-social-login, une dépendance tierce de plus pour la seule fermeture automatique de la fenêtre.@aparajita/capacitor-secure-storage: les jetons dans le trousseau d'iOS et le Keystore d'Android. Capacitor n'a pas de plugin officiel pour ça (@capacitor/preferencesécrit en clair), et un plugin natif à nous demanderait du Swift et du Kotlin à maintenir.
Dépendances du backend
La BCL .NET, les paquets Microsoft (dont EF Core), Npgsql, EFCore.NamingConventions pour nommer tables et colonnes en snake_case (maintenu par un membre de l'équipe EF). Acceptés par Samuel le 30 octobre 2026. StyleCop.Analyzers, d'abord accepté pour l'ordre des membres, a été écarté pendant TECH-07 : sa règle d'ordre contredit la nôtre, et notre analyseur la vérifie. Toute autre dépendance s'ajoute ici avec sa raison, avant d'être utilisée. Règles complètes dans Architecture du backend.
Job de popularité
Acceptés par Samuel le 2 novembre 2026, pour TECH-14. Le job mensuel qui compte les auditeurs de chaque œuvre (src/popularity/, voir Architecture du backend) est en Python, à part de l'API : Polars n'a pas de bibliothèque .NET officielle, et ce calcul sert aussi à essayer Polars sur un vrai volume.
- Python 3.14 : sa bibliothèque standard lit le
.tar.zstdu dump canonique (compression.zstd), sans dépendance de plus. - Polars : les 3 milliards d'écoutes, réduites fichier par fichier, puis comptées par paquets de personnes. Une analyse complète tient en 5 Go de mémoire et 6 minutes.
- psycopg (version 3) :
COPYvers la base de Musiqud et depuis la base du miroir. - pytest, pour ses tests (
tests/popularity/, lancés partests/run.sh popularity).
Ils tournent sur le poste de travail, pas en CI : tests/run.sh (voir le README à la racine). Tout est rangé dans tests/.
- API : xUnit v3, contre le Postgres de l'environnement local.
- App : Vitest et Testing Library, pour les composants, dans jsdom.
- Parcours : Playwright, en Chromium sur ordinateur et sur mobile. Les mêmes tests visent l'environnement local ou la version déployée.
Hébergement
- Visé : Kubernetes. Le fournisseur de production sera choisi vers M3, quand le nom du produit sera fixé.
- Preuve de concept : k3s sur le serveur de Samuel, sous
*.listner.io. Le routeur redirige les ports 80 et 443. Une CronJob tient l'enregistrement DNS à jour dans Netlify DNS, et Traefik obtient les certificats de Let's Encrypt. Le tunnel Cloudflare a été écarté. Procédure dansdeploy/k8s/README.md. - Images : GitHub Actions les construit pour amd64 et les publie sur GitHub Container Registry à chaque push sur
main, seulement celles dont les sources ont changé. C'est le seul usage de la CI : le compte GitHub gratuit a un quota de minutes. - Backlog : Plane, dans le même cluster, sous
plane.listner.io.
Questions ouvertes
- Spotify. Avec 5 utilisateurs en mode développement, il ne sert ni à la connexion ni à un import ouvert à tous. Le reprendre seulement si Spotify assouplit ses règles, ou comme import réservé à quelques comptes.
- Production, vers M3. Quel fournisseur Kubernetes? Quel domaine, puisque le nom Musiqud est provisoire? La base sera-t-elle un service géré ou dans le cluster?