veza/apps/web/dev_audit/frontend/01_architecture_analysis.md
senke 230286e073 refactor: Phase 1 — SUMI token foundation
- Rewrite index.css with complete SUMI token system (dark + light themes)
- All --sumi-* variables: backgrounds, surfaces, borders, text, pigments,
  spacing, radius, shadows, glass, scrollbar, motion, z-index, layout
- shadcn/Radix semantic mapping (--background, --foreground, etc.)
- Tailwind @theme mapping with new fonts (Inter, Space Grotesk, JetBrains Mono)
- SUMI keyframe animations (sumi-fade-in, sumi-slide-up, sumi-scale-in, etc.)
- Delete 11 redundant CSS files (design-system.css, design-tokens.css,
  button.css, card.css, input.css, badge-avatar.css, header.css,
  fix-input-focus.css, fix-login-form.css, visual-enhancements.css,
  premium-utilities.css)
- Update main.tsx: single CSS import (index.css only)
- Update ThemeProvider: data-theme attribute instead of .dark class toggle
- Update index.html FOUC script: data-theme attribute

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 01:48:01 +01:00

16 KiB
Raw Blame History

PHASE A — ARCHITECTURE FRONTEND

Scope : apps/web/src/ Analyse exhaustive : structure, responsabilités, state, requêtes réseau, routing, erreurs


A1. Structure des dossiers

Séparation features / shared / core

Le projet adopte un modèle hybride en transition :

Dossier Rôle Fichiers estimés Verdict
src/features/ Feature modules (auth, player, tracks, playlists, chat, etc.) ~900+ Bien structuré
src/components/ Composants "legacy" / partagés ~500+ ⚠️ Méga-dossier, 40+ sous-dossiers
src/components/ui/ Primitives UI (Button, Modal, Tabs, etc.) ~200+ Bonne séparation
src/services/ Services partagés (API client, websocket, cache) ~30+ Bien isolés
src/stores/ Stores Zustand globaux ~6 fichiers Correct
src/hooks/ Hooks partagés ~15+ Bien isolés
src/utils/ Utilitaires transverses ~20+ Correct
src/schemas/ Schemas Zod ~5 fichiers Bonne pratique
src/types/ Types globaux + generated ~10 fichiers Correct

Problème majeur : dualité components/ vs features/

Duplication de domaines détectée :

Domaine components/ features/ Problème
Player components/player/ features/player/ Deux emplacements concurrents
Settings components/settings/ (7 sous-dossiers) features/settings/ Même domaine, deux endroits
Auth components/auth/ features/auth/ Confusion sur le canonique
Social components/social/ Devrait être dans features/
Education components/education/ Devrait être dans features/
Commerce components/commerce/ Devrait être dans features/
Gamification components/gamification/ Pas de feature associée
Studio components/studio/ features/studio/ Deux emplacements
Search components/search/ features/search/ Deux emplacements

Verdict : La migration vers une architecture feature-first est en cours mais incomplète. Environ 60% des domaines métier ont leur module dans features/, mais des vestiges significatifs restent dans components/. [src/components/:ensemble] [src/features/:ensemble]

Barrel exports

Les barrel exports (index.ts) sont présents et cohérents dans les modules features decomposés :

  • features/tracks/components/comment-thread/index.ts
  • features/playlists/components/playlist-list/index.ts
  • features/player/components/player-bar/index.ts
  • Tous les sous-composants UI décomposés (accordion, dialog, tabs, etc.)

Manquants : les composants de components/ n'ont pas toujours de barrel export cohérent.

Composants groupés par feature vs par type

Le pattern dominant est par feature dans features/, et par type/domaine dans components/. L'architecture cible est clairement feature-first.

Routes colocalisées

Les routes sont séparées dans src/router/ — pas colocalisées avec les features. C'est acceptable avec React Router v6, mais les features contiennent leurs propres pages/ (ex: features/tracks/pages/, features/auth/pages/) — bonne pratique.

Dossiers morts / orphelins potentiels

Dossier suspect Raison
components/views/ 20+ sous-dossiers de "views" qui ne sont pas des routes — probablement des prototypes Storybook
components/demo/ Dossier de démo — mort probable
components/base/ Composants "base" — potentiellement redondant avec components/ui/
stories/ (racine) Stories globales + assets Storybook default — à nettoyer

A2. Séparation des responsabilités

Tableau des composants > 100 lignes

Composant Lignes Type Responsabilités mélangées ? Verdict
components/developer/DeveloperDashboardView.tsx 323 Container Oui — API calls, state, rendering 🔴 REFACTOR
features/dashboard/pages/DashboardPage.tsx 329 Container Oui — Data fetching, state, rendering 🔴 REFACTOR
components/layout/Sidebar.tsx 295 Presentation Non — Pure UI OK
components/developer/SwaggerUI.tsx 279 Hybrid Oui — Config, error handling, rendering 🟡 REFACTOR
components/upload/metadata/MetadataForm.tsx 274 Container Oui — State, modals, form logic 🟡 REFACTOR
features/chat/components/ChatMessages.tsx 218 Container Oui — Store access, state, rendering 🟡 REFACTOR
app/App.tsx 192 Hybrid Oui — Auth init, theme, i18n, CSRF 🟡 REFACTOR
features/tracks/pages/track-detail-page/TrackDetailPageInfo.tsx 178 Presentation Non — Pure UI OK
components/layout/Header.tsx 172 Hybrid Oui — Auth, theme, navigation 🟡 REFACTOR
features/player/components/GlobalPlayer.tsx 154 Container ⚠️ Partiel — Near limit OK (à surveiller)
features/tracks/components/TrackSearch.tsx 147 Container Oui — Search logic, API calls 🟡 REFACTOR
components/forms/LoginForm.tsx 135 Hybrid ⚠️ Partiel — Form pattern acceptable OK
features/tracks/pages/track-detail-page/TrackDetailPageCoverAndActions.tsx 133 Presentation Non — Pure UI OK
features/playlists/components/PlaylistHeader.tsx 130 Presentation Non — Pure UI OK
features/chat/pages/ChatPage.tsx 114 Container Oui — API calls, state, rendering 🟡 REFACTOR
features/tracks/components/track-filters/TrackFilters.tsx 103 Hybrid ⚠️ Partiel — Uses hook OK
features/player/components/PlayerControls.tsx 106 Presentation Non — Pure UI OK
features/tracks/components/TrackList.tsx 291 Presentation Non — Pure UI, 18 props OK

Synthèse :

  • 27% des composants > 100 lignes nécessitent un refactoring
  • 40% mélangent des responsabilités (au moins partiellement)
  • 97% ont leurs props typées — excellent
  • Le pattern useXxxPage() hook est bien adopté dans les features récentes

Bonne pratique observée

Les pages récentes utilisent un hook dédié pour la logique :

  • features/tracks/pages/track-detail-page/useTrackDetailPage.tsTrackDetailPage.tsx
  • features/profile/pages/user-profile-page/useUserProfilePage.tsUserProfilePage.tsx
  • features/library/pages/library-page/useLibraryPage.tsLibraryPage.tsx

Ce pattern de séparation est exemplaire et devrait être généralisé aux composants plus anciens.


A3. Gestion d'état

Couches de state identifiées

Couche Technologie Nombre Scope
State local useState ~200+ fichiers Composant
Context API createContext 4 contextes Sous-arbre React
Store global Zustand 7 stores Application
Server state TanStack React Query ~50+ hooks Cache serveur
URL state React Router Params + query URL

Stores Zustand détaillés

Store Fichier State contenu Middlewares Persisté
UI Store stores/ui.ts theme, language, sidebarOpen, notifications persist, devtools, broadcastSync Oui (theme, language, sidebarOpen)
Library Store stores/library.ts filters persist, devtools Oui (filters)
Cart Store stores/cartStore.ts items[], cart operations persist Oui
Rate Limit Store stores/rateLimit.ts Rate limit headers persist Oui
Auth Store features/auth/store/authStore.ts isAuthenticated, isLoading, error persist, broadcastSync Oui (isAuthenticated)
Chat Store features/chat/store/chatStore.ts userId, conversations[], messages, typingUsers, WS state devtools, immer Non
Player Store features/player/store/playerStore.ts currentTrack, isPlaying, queue[], volume, repeat, shuffle persist Oui (volume, queue, etc.)

Contextes React

Contexte Fichier Contenu Hook
AuthContext context/AuthContext.tsx user, isAuthenticated, isLoading, login, register, logout useAuth()
ToastContext components/feedback/ToastProvider.tsx toasts[], addToast, removeToast useToast()
ThemeContext components/theme/ThemeProvider.tsx theme, setTheme useTheme()
AudioContext context/audio-context/AudioContext.tsx Audio playback context useAudio()

Diagnostic

Conflits entre couches :

  • ⚠️ Auth : AuthContext (Context API) et authStore (Zustand) gèrent l'authentification — duplication potentielle. Le contexte gère user + isAuthenticated, le store aussi isAuthenticated. Source de vérité ambiguë. [context/AuthContext.tsx] [features/auth/store/authStore.ts]
  • Player : Zustand store uniquement — pas de conflit
  • Server data : React Query pour les données serveur — bien séparé du state UI

State normalisé ? :

  • ⚠️ Le chat store contient conversations[] et messages{} — normalisé par conversation, mais les messages sont dans un Map — acceptable pour le chat
  • Les données serveur (tracks, playlists) sont gérées par React Query (cache automatique)
  • Pas de normalisation explicite des entités côté client (pas de store normalisé type normalizr)

Prop drilling > 3 niveaux :

  • Pas de chaîne évidente détectée. Les données descendent via hooks (React Query) ou stores (Zustand), rarement par props directes sur plus de 2 niveaux. [DONNÉES INSUFFISANTES — nécessite analyse AST complète]

A4. Gestion des requêtes réseau

Architecture API

Client centralisé : services/api/client.ts (2 237 lignes) — Axios-based avec intercepteurs complets.

Features du client :

  • Token refresh automatique sur 401 avec queue de requêtes
  • CSRF token injection avec retry sur 403
  • Validation requêtes/réponses via Zod schemas
  • Retry logic : 3 retries, exponential backoff (1s → 10s max)
  • Request deduplication pour GET
  • Response caching pour GET
  • Rate limit tracking
  • Slow request detection (seuil 1000ms)
  • Offline queue support

Inventaire des services

56 fichiers service/API répartis entre services/ (partagés) et features/*/services/ (feature-specific).

Tableau des patterns d'appels majeurs

Fichier Pattern Loading Error Cancel Typé Centralisé
services/api/client.ts Axios + interceptors Centralisé AbortController
services/api/auth.ts apiClient.post/get Via RQ Via interceptor
features/tracks/api/trackApi.ts apiClient wrappers Via RQ Via interceptor
features/playlists/services/playlistService.ts apiClient.get/post Via RQ Via interceptor
features/tracks/services/uploadService.ts apiClient.post + FormData useState try/catch custom
services/websocket.ts WebSocket natif Manual Reconnect auto ⚠️ any×8
features/streaming/services/hlsService.ts HLS.js Via RQ Via HLS.js Spécifique

React Query configuration

  • staleTime: 1 minute
  • gcTime: 5 minutes
  • retry: false (délégué au retry Axios)
  • refetchOnWindowFocus: false
  • refetchOnReconnect: true

Patterns d'erreur

  • 3 patterns coexistent :
    1. Try/catch avec custom error types (trackService)
    2. Propagation directe (erreurs gérées par l'intercepteur)
    3. handleApiServiceError() utility wrapper
  • Incohérence notable : certains services gèrent les erreurs localement, d'autres délèguent entièrement

AbortController

  • Implémenté dans le client (createCancellableRequest(), createRequestWithTimeout())
  • ⚠️ Faiblement adopté dans les services individuels
  • ⚠️ React Query hooks n'utilisent pas systématiquement le signal

A5. Routing

Solution

  • React Router v6 (react-router-dom ^6.22.0)
  • BrowserRouter avec future flags activés [src/main.tsx:227]

Routes

Type Nombre Lazy-loaded
Routes publiques (auth) 5 Toutes
Routes publiques standalone 2 Toutes
Routes protégées 25 Toutes
Routes d'erreur 2 Toutes
Catch-all (*) 1 Redirect → /404
Total 35 100% lazy

Guards d'authentification

  • ProtectedRoute : vérifie isAuthenticated + TokenStorage.getAccessToken() → redirect /login [components/auth/ProtectedRoute.tsx]
  • PublicRoute : redirige les utilisateurs connectés vers /dashboard [router/PublicRoute.tsx]
  • ProtectedLayoutRoute : wraps routes protégées avec DashboardLayout [router/ProtectedLayoutRoute.tsx]

404 / Fallback

  • Route /404 avec composant LazyNotFound
  • Route /500 avec composant LazyServerError
  • Catch-all * → redirect /404
  • Error boundaries wrappent les routes d'erreur

Lazy loading

100% des routes sont lazy-loaded via createLazyComponent [components/ui/lazy-component/lazyExports.ts:3-235]. Implémentation basée sur React.lazy() + Suspense + LazyErrorBoundary — excellente configuration.

Deep linking

  • Routes bookmarkables (paths sémantiques : /tracks/:id, /playlists/*, /u/:username)
  • Paramètres URL utilisés (useParams, useSearchParams)

A6. Gestion des erreurs

Error boundaries

Boundary Fichier Scope Sentry
Main ErrorBoundary components/ui/ErrorBoundary.tsx Routes + app
Legacy ErrorBoundary components/ErrorBoundary.tsx Backup
LazyErrorBoundary components/ui/lazy-component/LazyErrorBoundary.tsx Lazy components
PlaylistErrorBoundary features/playlists/components/PlaylistErrorBoundary.tsx Feature playlist
  • Error boundary au niveau app (App.tsx wrappé)
  • Error boundaries au niveau routes
  • ⚠️ 2 implémentations ErrorBoundary (main + legacy) — duplication

Erreurs réseau

  • Feedback utilisateur via toast notifications (react-hot-toast)
  • Parsing centralisé (parseApiError()) avec messages user-friendly
  • Catégorisation : network, validation, auth, rate_limit, server_error, timeout
  • ⚠️ Inconsistance : inline error display vs toast-only selon les composants

Erreurs de formulaire

  • React Hook Form + Zod pour la validation client
  • Validation timing configurable (onBlur, onChange, onSubmit)
  • ⚠️ Validation serveur : certains formulaires ne remontent pas les erreurs serveur dans les champs

Logging frontend

  • Sentry configuré (lib/sentry.ts) avec :
    • Browser tracing
    • Session replay
    • Error filtering (prod uniquement si VITE_SENTRY_DSN défini)
    • Context enrichment via logger
  • Logger custom (utils/logger.ts) avec Sentry integration dynamique

Fallback UI

  • Skeleton loaders pour les chargements
  • ErrorDisplay component pour les erreurs
  • ComingSoon component pour les features non implémentées
  • Empty states gérés (composants *Empty.tsx dans les views)

SCORE ARCHITECTURE : 7/10

Points gagnés

Point Score Justification
Routing +1.0 100% lazy-loaded, guards, 404, deep linking — exemplaire
TypeScript strict +1.0 Config stricte complète, types omniprésents
Server state +1.0 React Query bien configuré, staleTime/gcTime cohérents
Error handling +0.8 Sentry + boundaries + parsing centralisé
Feature modules +0.7 features/ bien structuré pour les domaines principaux
API client +0.8 Client robuste : retry, dedup, cache, CSRF, offline queue
Hooks pattern +0.7 useXxxPage() pattern bien adopté dans les features récentes

Points perdus

Point Score Justification
Dualité components/features -1.0 40% des domaines ont des composants dans les deux dossiers
Auth state duplication -0.5 AuthContext + authStore — source de vérité ambiguë
as any endémique -0.5 706 as any casts — ESLint rule explicitement off
Client.ts monstre -0.5 2 237 lignes — devrait être décomposé en modules
Inconsistance error handling -0.3 3 patterns d'erreur coexistent dans les services
AbortController sous-utilisé -0.2 Implémenté mais faiblement adopté