Files
skycel ec1ae92799 🎉 Init monorepo Nuxt 4 + Laravel 12 (Story 1.1)
Setup complet de l'infrastructure projet :
- Frontend Nuxt 4 (SSR, TypeScript, i18n, Pinia, TailwindCSS)
- Backend Laravel 12 API-only avec middleware X-API-Key et CORS
- Design tokens (sky-dark, sky-accent, sky-text) et polices (Merriweather, Inter)
- Documentation planning et implementation artifacts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 02:08:56 +01:00

20 KiB

stepsCompleted, inputDocuments, workflowType, project_name, user_name, date
stepsCompleted inputDocuments workflowType project_name user_name date
1
2
3
4
docs/prd-gamification.md
docs/planning-artifacts/ux-design-specification.md
docs/brainstorming-gamification-2026-01-26.md
architecture skycel Célian 2026-02-01

Architecture Decision Document

This document builds collaboratively through step-by-step discovery. Sections are appended as we work through each architectural decision together.

Project Context Analysis

Requirements Overview

Functional Requirements: 14 FRs couvrant : double entrĂ©e visiteur (FR1), transitions animĂ©es seamless (FR2), narrateur-guide contextuel (FR3), carte interactive Konva.js (FR4), arbre de compĂ©tences vis.js (FR5), compĂ©tences cliquables → projets (FR6), dialogues PNJ typewriter (FR7), barre de progression globale (FR8), chemins narratifs multiples 4-8 parcours (FR9), challenge/puzzle avant contact (FR10), easter eggs cachĂ©s (FR11), sauvegarde LocalStorage (FR12), bilingue FR/EN avec dĂ©tection URL (FR13), contact comme rĂ©compense narrative (FR14).

Architecturalement, ces FRs dessinent un systÚme à forte interactivité cÎté client avec un backend API relativement simple (CRUD + contact + progression). La complexité réside dans l'orchestration frontend : état de progression, navigation narrative adaptative, et composants lourds en lazy-loading.

Non-Functional Requirements:

  • NFR1 : Bundle JS ≀ 170kb gzip (Nuxt + Konva + vis.js) avec lazy-loading
  • NFR2 : LCP < 2.5s sur 3G
  • NFR3 : Responsive avec expĂ©rience mobile adaptĂ©e (carte simplifiĂ©e)
  • NFR4 : Navigateurs modernes (Chrome, Firefox, Safari, Edge — 2 derniĂšres versions)
  • NFR5 : URLs SEO-friendly, contenu accessible aux crawlers (SSR)
  • NFR6 : Respect prefers-reduced-motion (accessibilitĂ© animations)
  • NFR7 : i18n SSR via @nuxtjs/i18n avec fichiers JSON
  • NFR8 : Images WebP avec lazy loading

Les NFRs les plus structurants pour l'architecture sont le budget JS (NFR1), le SSR pour SEO (NFR5/NFR7), et le responsive avec deux paradigmes de navigation (NFR3).

Scale & Complexity:

  • Domaine principal : Full-stack web (Nuxt 4 SSR + Laravel 12 API REST)
  • Niveau de complexitĂ© : Moyenne-haute — richesse des interactions frontend, faible volume de donnĂ©es
  • Composants architecturaux estimĂ©s : ~15-20 (pages, composants custom, stores, composables, API endpoints, modĂšles)

Technical Constraints & Dependencies

  • Nuxt 4 SSR : Impose une architecture hybride serveur/client avec nouvelle structure app/. Les composants Konva.js et vis.js doivent ĂȘtre exclusivement client-side (.client.vue)
  • Laravel 12 API-only : Backend dĂ©couplĂ©, communication via API REST JSON. CORS requis. Upgrade vers Laravel 13 prĂ©vu dĂšs sa sortie stable (Q1 2026)
  • MariaDB : SchĂ©ma relationnel dĂ©fini dans le brainstorming (7 tables). Migration vers Eloquent ORM
  • Budget JS 170kb : Konva (~50kb) + vis-network (~50kb) + Nuxt (~50kb) = marge trĂšs faible. StratĂ©gie de lazy-loading critique
  • Monorepo : /frontend (Nuxt) + /api (Laravel) dans le mĂȘme repo — dĂ©cision validĂ©e pour un projet solo avec frontend/backend fortement couplĂ©s
  • HĂ©bergement dual : Node.js pour Nuxt SSR + PHP 8.2+ pour Laravel — deux runtimes distincts

Cross-Cutting Concerns Identified

  1. Gestion d'Ă©tat & progression : Le store Pinia useProgressionStore irrigue toute l'application — carte, narrateur, barre XP, dĂ©blocage contact, easter eggs. Doit ĂȘtre persistĂ© (LocalStorage) et compatible SSR
  2. Internationalisation (i18n) : Bilingue FR/EN Ă  travers toutes les couches — SSR, API responses, textes narrateur, dialogues PNJ, challenges. StratĂ©gie prefix_except_default pour URLs
  3. SystÚme de héros : Le choix du personnage (Recruteur/Client/Dev) impacte le vouvoiement, le ton du narrateur, le contenu des challenges, et potentiellement l'ordre des suggestions. Transversal à toute la couche de présentation
  4. Accessibilité (WCAG AA) : Contraste, navigation clavier, prefers-reduced-motion, screen readers, skip links. Impacte chaque composant custom (carte, PNJ, narrateur, skill tree)
  5. Performance & lazy-loading : Composants lourds (Konva, vis.js) chargés à la demande. Images WebP, fonts variables, SSR pour le premier rendu. Budget strict

Starter Template Evaluation

Primary Technology Domain

Full-stack web (Nuxt 4 SSR + Laravel API REST) basé sur l'analyse des exigences projet.

Starter Options Considered

Option Version Statut Notes
Nuxt 4 4.3+ Stable (juillet 2025) Nouvelle structure app/, TypeScript strict, data fetching amélioré
Nuxt 3 3.21 EOL juillet 2026 ÉcartĂ© — fin de vie trop proche pour un nouveau projet
Laravel 12 12.x Stable (février 2025) Release de maintenance, support bugs jusqu'en août 2026
Laravel 13 13.x Imminent (Q1 2026) PHP 8.3+, support jusqu'en 2028. Upgrade depuis 12 attendu facile

Selected Starters

Frontend : Nuxt 4

Rationale : Version stable et actuelle. Structure app/ plus propre, meilleur TypeScript, Nuxt 3 en fin de vie. Tous les modules clés (@nuxtjs/i18n, @pinia/nuxt, @nuxtjs/tailwindcss, nuxt/image, @nuxtjs/sitemap) sont compatibles Nuxt 4.

Initialization Command :

npx nuxi@latest init frontend

Backend : Laravel 12 (upgrade vers 13 dĂšs sa sortie)

Rationale : Stable et maintenu. Laravel 13 est imminent mais pas encore sorti. Démarrer sur 12 avec PHP 8.2+ permet de commencer immédiatement. L'upgrade vers 13 sera minimal.

composer create-project laravel/laravel api

Architectural Decisions Provided by Starters

Nuxt 4 fournit :

  • Structure app/ avec auto-imports (components, composables, utils)
  • SSR natif avec hydration client
  • Routing fichier-based (app/pages/)
  • Nitro comme serveur (build optimisĂ©)
  • TypeScript par dĂ©faut
  • DevTools intĂ©grĂ©s

Laravel 12 fournit :

  • Structure MVC avec Eloquent ORM
  • Routing API (routes/api.php)
  • Migration system pour le schĂ©ma BDD
  • Form Requests pour la validation
  • API Resources pour les transformations JSON
  • Rate limiting, CORS, middleware stack
  • Pest/PHPUnit pour les tests

Structure Monorepo (Nuxt 4)

skycel/
├── frontend/                   # Application Nuxt 4
│   ├── app/                    # Code applicatif (structure Nuxt 4)
│   │   ├── pages/
│   │   ├── components/
│   │   ├── composables/
│   │   ├── stores/
│   │   ├── layouts/
│   │   ├── plugins/
│   │   ├── assets/
│   │   └── app.vue
│   ├── server/                 # Server routes/API Nuxt (si besoin)
│   ├── public/
│   ├── i18n/
│   ├── nuxt.config.ts
│   └── package.json
├── api/                        # Backend Laravel 12
│   ├── app/
│   ├── database/
│   ├── routes/
│   ├── config/
│   ├── tests/
│   └── composer.json
├── docs/                       # Documentation projet
└── README.md

Third-Party Services

Service Solution Intégration
Email PHPMailer via Laravel Mail Backend
Anti-spam Google reCAPTCHA v3 Frontend + Backend validation
Images nuxt/image + Sharp Frontend (local)
Sitemap @nuxtjs/sitemap Frontend
Analytics Matomo (self-hosted) Frontend script
Error tracking Sentry Frontend + Backend
Monitoring Uptime Kuma Externe (existant)
Backups BDD Script cron mysqldump Serveur

Core Architectural Decisions

Decision Priority Analysis

Critical Decisions (Block Implementation) :

  • StratĂ©gie i18n hybride (JSON statique + table translations centralisĂ©e)
  • Architecture API REST avec API Key + CORS strict
  • Structure composants frontend (ui / feature / layout)
  • StratĂ©gie lazy-loading pour respecter le budget JS ≀ 170kb
  • Store Pinia de progression avec persistance LocalStorage

Important Decisions (Shape Architecture) :

  • Abandon Swup.js → transitions Nuxt natives + GSAP
  • Double validation frontend + backend
  • Format de rĂ©ponse API Resources avec enveloppe standard
  • Bandeau RGPD intĂ©grĂ© Ă  l'immersion narrative
  • Environnement staging avec sous-domaine

Deferred Decisions (Post-MVP) :

  • Endpoints CRUD admin protĂ©gĂ©s par tokens exclusifs (aprĂšs MVP)
  • Upgrade Laravel 12 → 13 (dĂšs sortie stable)
  • Sauvegarde cloud de progression via email (Phase 2)

Data Architecture

Stratégie i18n : Hybride

  • Contenu statique UI : Fichiers JSON via @nuxtjs/i18n (i18n/fr.json, i18n/en.json). Labels, boutons, messages d'interface, textes de navigation
  • Contenu dynamique : Table translations centralisĂ©e en MariaDB. Les tables mĂ©tier (projects, skills, testimonials, narrator_texts, easter_eggs) stockent des clĂ©s i18n (title_key, text_key). La table translations contient les valeurs par langue
  • Rationale : FlexibilitĂ© pour ajouter une langue sans modifier le schĂ©ma. SĂ©paration claire entre UI (dĂ©ployĂ©e avec le frontend) et contenu (gĂ©rĂ© via API/BDD)

Schéma table translations :

CREATE TABLE translations (
    id INT AUTO_INCREMENT PRIMARY KEY,
    lang VARCHAR(5) NOT NULL,
    key_name VARCHAR(255) NOT NULL,
    value TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY unique_translation (lang, key_name),
    INDEX idx_lang (lang)
);

Cache : File cache Laravel

  • Driver : file (config CACHE_DRIVER=file)
  • Suffisant pour la volumĂ©trie d'un portfolio (faible nombre de requĂȘtes, peu de donnĂ©es)
  • Pas de dĂ©pendance externe (Redis non requis)

Validation : Double validation

  • Frontend (Nuxt) : Validation lĂ©gĂšre en temps rĂ©el pour l'UX (champs requis, format email, longueur). Via les composables Vue ou VeeValidate
  • Backend (Laravel) : Validation complĂšte via Form Requests. Source de vĂ©ritĂ© pour la sĂ©curitĂ©. Rejette toute donnĂ©e invalide avec rĂ©ponse 422

Authentication & Security

Protection formulaire de contact :

  • Google reCAPTCHA v3 (invisible, score-based) cĂŽtĂ© frontend
  • Honeypot field (champ cachĂ©) comme seconde couche
  • Rate limiting Laravel : 5 requĂȘtes/minute par IP sur POST /api/contact

Sécurité API :

  • API Key : Token partagĂ© entre Nuxt et Laravel via header X-API-Key. StockĂ© dans les .env des deux applications. Middleware Laravel vĂ©rifie la prĂ©sence et validitĂ© du token sur chaque requĂȘte
  • CORS strict : N'accepte que le domaine du frontend (Access-Control-Allow-Origin: https://skycel.fr)
  • Endpoints CRUD admin (post-MVP) : ProtĂ©gĂ©s par tokens exclusifs diffĂ©rents de l'API Key publique. Middleware dĂ©diĂ© avec permissions granulaires

Protection des données visiteur :

  • Session ID : UUID v4 gĂ©nĂ©rĂ© cĂŽtĂ© client, stockĂ© en LocalStorage
  • Email : Optionnel, uniquement pour la sauvegarde cloud de progression
  • Pas de tracking sans consentement

RGPD :

  • Bandeau de consentement intĂ©grĂ© Ă  l'immersion narrative (dialogue PNJ ou narrateur araignĂ©e, style "pacte d'aventurier")
  • Consentement requis avant activation de Matomo et stockage LocalStorage de progression
  • État du consentement stockĂ© dans le store Pinia (consentGiven) et persistĂ© en LocalStorage

API & Communication Patterns

Design pattern : REST classique

Endpoints publics (lecture) :

Méthode Endpoint Description
GET /api/projects Liste des projets
GET /api/projects/{slug} Détail d'un projet
GET /api/skills Arbre de compétences
GET /api/testimonials Témoignages PNJ
GET /api/narrator/{context} Textes narrateur par contexte
GET /api/easter-eggs Métadonnées easter eggs (pas les réponses)
GET /api/progress/{session_id} Récupérer progression
POST /api/progress Sauvegarder progression
POST /api/contact Formulaire contact (rate limited + reCAPTCHA)

Endpoints admin CRUD (post-MVP, tokens exclusifs) :

Méthode Endpoint Description
POST /api/admin/projects Créer un projet
PUT /api/admin/projects/{id} Modifier un projet
DELETE /api/admin/projects/{id} Supprimer un projet
idem pour skills, testimonials, narrator, easter-eggs CRUD complet

Gestion de la langue : Header Accept-Language

  • Le frontend Nuxt envoie Accept-Language: fr ou Accept-Language: en dans chaque requĂȘte API
  • Middleware Laravel extrait la langue et la passe au query builder pour joindre la table translations
  • Fallback : fr si header absent ou langue non supportĂ©e

Format de réponse : Laravel API Resources

Réponse standard :

{
  "data": [
    { "id": 1, "slug": "skycel", "title": "Skycel Portfolio", "..." : "..." }
  ],
  "meta": {
    "total": 5,
    "lang": "fr"
  }
}

Gestion d'erreurs : Format standard

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Le champ email est requis",
    "details": {}
  }
}

Codes HTTP : 400 (bad request), 401 (API key invalide), 404 (not found), 422 (validation), 429 (rate limit), 500 (erreur serveur)

Frontend Architecture

Architecture des composants :

app/components/
├── ui/                        # Composants atomiques rĂ©utilisables
│   ├── BaseButton.vue
│   ├── BaseBadge.vue
│   ├── BaseModal.vue
│   └── ...
├── feature/                   # Composants mĂ©tier
│   ├── NarratorDialogue.vue
│   ├── PnjCard.vue
│   ├── SkillTree.client.vue   # Client-only (vis-network)
│   ├── InteractiveMap.client.vue  # Client-only (Konva.js)
│   ├── ProgressBar.vue
│   ├── HeroSelector.vue
│   ├── ChallengePanel.vue
│   └── EasterEgg.vue
├── layout/                    # Structure de page
│   ├── AppHeader.vue
│   ├── AppFooter.vue
│   ├── ConsentBanner.vue      # RGPD immersif
│   └── NarratorOverlay.vue

Stratégie lazy-loading :

Couche Chargement Poids estimé (gzip)
Nuxt core + Vue + Pinia Immédiat ~50kb
TailwindCSS (purgé) Immédiat ~10kb
Pages Lazy (navigation) ~5-10kb/page
Konva.js Lazy (page carte desktop uniquement) ~50kb
vis-network Lazy (page skills uniquement) ~50kb
GSAP Lazy (premiĂšre animation complexe) ~25kb
reCAPTCHA v3 Lazy (page contact uniquement) Externe

Budget initial (premier chargement) : ~60-70kb gzip — bien sous le budget de 170kb. Les librairies lourdes ne se chargent qu'à la demande.

Store Pinia useProgressionStore :

interface ProgressionState {
  sessionId: string              // UUID v4
  hero: 'recruteur' | 'client' | 'dev' | null
  currentPath: string            // Chemin narratif actuel
  visitedSections: string[]      // Sections visitées
  completionPercent: number      // 0-100
  easterEggsFound: string[]      // Slugs des easter eggs trouvés
  challengeCompleted: boolean
  contactUnlocked: boolean
  narratorStage: number          // 1-5 (évolution de l'araignée)
  choices: Record<string, string> // Choix narratifs
  consentGiven: boolean          // RGPD
}
  • Persistance : pinia-plugin-persistedstate → LocalStorage
  • Synchronisation API : POST /api/progress dĂ©clenchĂ© quand le visiteur fournit son email (sauvegarde cloud optionnelle)
  • CompatibilitĂ© SSR : Le store s'initialise vide cĂŽtĂ© serveur, se rĂ©hydrate cĂŽtĂ© client depuis LocalStorage

Transitions et animations :

  • Transitions de page : SystĂšme natif Nuxt/Vue (<NuxtPage> + <Transition>) avec CSS animations
  • Animations complexes : GSAP (narrateur araignĂ©e, rĂ©vĂ©lation progressive, transitions de zone immersives)
  • Swup.js : AbandonnĂ© — redondant avec les transitions Nuxt natives et potentiellement conflictuel
  • prefers-reduced-motion : RespectĂ© via media query, animations rĂ©duites ou dĂ©sactivĂ©es

Infrastructure & Deployment

Architecture serveur :

                    ┌─────────────────────────┐
                    │     Nginx (port 80/443) │
                    │   SSL + gzip + cache    │
                    └─────────┬───────────────┘
                              │
                ┌─────────────┮─────────────┐
                │                           │
                ▌                           ▌
    ┌───────────────────┐       ┌───────────────────┐
    │   Node.js :3000   │       │   PHP-FPM :9000   │
    │   Nuxt 4 SSR      │       │   Laravel 12 API  │
    └───────────────────┘       └───────────────────┘
                                        │
                                        ▌
                                ┌───────────────────┐
                                │   MariaDB :3306   │
                                └───────────────────┘
  • Nginx dispatch : /api/* → PHP-FPM, tout le reste → Node.js (Nuxt SSR)
  • SSL via Let's Encrypt (certbot)
  • Compression gzip activĂ©e
  • Headers de cache pour les assets statiques

Gestion des environnements :

  • Production : skycel.fr — branche prod
  • Staging : staging.skycel.fr — branche staging ou main
  • Fichiers .env distincts par environnement et par application (frontend/.env.production, frontend/.env.staging, api/.env.production, api/.env.staging)

CI/CD : Script deploy.sh manuel

# Déploiement déclenché manuellement
# Se base sur la branche 'prod'
./deploy.sh [production|staging]

Le script automatise :

  1. git pull origin prod (ou staging)
  2. cd frontend && npm install && npm run build
  3. cd api && composer install --no-dev && php artisan migrate --force
  4. php artisan config:cache && php artisan route:cache
  5. Restart du process Node.js (PM2 ou systemd)
  6. Notification de succÚs/échec

Backups BDD :

  • Cron quotidien Ă  3h00 : mysqldump complet de la base skycel
  • RĂ©tention locale : 7 jours (rotation automatique, suppression des dumps > 7j)
  • RĂ©plication : Copie automatique vers un serveur distant via rsync ou scp aprĂšs chaque dump
  • Nommage : skycel_backup_YYYY-MM-DD_HH-MM.sql.gz (compressĂ©)

Decision Impact Analysis

Séquence d'implémentation recommandée :

  1. Initialisation monorepo (Nuxt 4 + Laravel 12)
  2. Configuration Nginx + environnements (.env, staging)
  3. Schéma BDD + migrations + table translations
  4. API endpoints publics (lecture) + middleware API Key + CORS
  5. Store Pinia progression + persistance LocalStorage
  6. Composants layout + transitions Nuxt natives
  7. Pages et composants feature (par epic)
  8. Intégrations tierces (reCAPTCHA, Matomo, Sentry)
  9. Script deploy.sh + cron backup
  10. Endpoints CRUD admin (post-MVP)

Dépendances inter-composants :

  • Le store Pinia dĂ©pend du schĂ©ma de progression (BDD + API)
  • Les composants feature dĂ©pendent de l'API (endpoints + format de rĂ©ponse)
  • L'i18n frontend dĂ©pend de la table translations (contenu dynamique)
  • Le bandeau RGPD doit ĂȘtre en place avant l'activation de Matomo
  • Le lazy-loading des composants lourds dĂ©pend de la structure de routing Nuxt