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>
20 KiB
stepsCompleted, inputDocuments, workflowType, project_name, user_name, date
| stepsCompleted | inputDocuments | workflowType | project_name | user_name | date | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
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
- Gestion d'état & progression : Le store Pinia
useProgressionStoreirrigue toute l'application â carte, narrateur, barre XP, dĂ©blocage contact, easter eggs. Doit ĂȘtre persistĂ© (LocalStorage) et compatible SSR - Internationalisation (i18n) : Bilingue FR/EN Ă travers toutes les couches â SSR, API responses, textes narrateur, dialogues PNJ, challenges. StratĂ©gie
prefix_except_defaultpour URLs - 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
- Accessibilité (WCAG AA) : Contraste, navigation clavier,
prefers-reduced-motion, screen readers, skip links. Impacte chaque composant custom (carte, PNJ, narrateur, skill tree) - 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é |
| 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 |
|---|---|---|
| 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
translationscentralisée en MariaDB. Les tables métier (projects, skills, testimonials, narrator_texts, easter_eggs) stockent des clés i18n (title_key,text_key). La tabletranslationscontient 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(configCACHE_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.envdes 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: frouAccept-Language: endans chaque requĂȘte API - Middleware Laravel extrait la langue et la passe au query builder pour joindre la table
translations - Fallback :
frsi 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/progressdé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â brancheprod - Staging :
staging.skycel.frâ branchestagingoumain - Fichiers
.envdistincts 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 :
git pull origin prod(ou staging)cd frontend && npm install && npm run buildcd api && composer install --no-dev && php artisan migrate --forcephp artisan config:cache && php artisan route:cache- Restart du process Node.js (PM2 ou systemd)
- Notification de succÚs/échec
Backups BDD :
- Cron quotidien Ă 3h00 :
mysqldumpcomplet de la base skycel - Rétention locale : 7 jours (rotation automatique, suppression des dumps > 7j)
- Réplication : Copie automatique vers un serveur distant via
rsyncouscpaprĂšs chaque dump - Nommage :
skycel_backup_YYYY-MM-DD_HH-MM.sql.gz(compressé)
Decision Impact Analysis
Séquence d'implémentation recommandée :
- Initialisation monorepo (Nuxt 4 + Laravel 12)
- Configuration Nginx + environnements (.env, staging)
- Schéma BDD + migrations + table translations
- API endpoints publics (lecture) + middleware API Key + CORS
- Store Pinia progression + persistance LocalStorage
- Composants layout + transitions Nuxt natives
- Pages et composants feature (par epic)
- Intégrations tierces (reCAPTCHA, Matomo, Sentry)
- Script deploy.sh + cron backup
- 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