Frontend SvelteKit
Le dépôt web utilise SvelteKit 2, Svelte 5, TypeScript strict, Tailwind CSS v4, Paraglide, Vitest, Playwright et Storybook. Il rend le site public, le back-office et les endpoints BFF.
Organisation
Section intitulée « Organisation »src/├── lib/│ ├── domain/<feature>/ # entités et ports TypeScript purs│ ├── application/<feature>/ # cas d’usage│ ├── infrastructure/<feature>/ # repositories HTTP et types contractuels│ ├── presentation/<feature>/ # composants publics et view models│ ├── presentation/backoffice/ # présentation administrative│ ├── components/ui/ # primitives shadcn-svelte générées│ ├── server/ # helpers impossibles à importer côté client│ └── container.ts # assemblage des implémentations├── routes/│ ├── (app)/ # routes publiques│ ├── back-office/ # pages administratives authentifiées│ ├── login/ # mot de passe, TOTP et invitations│ └── api/ # endpoints BFF└── app.css # tokens Tailwind publics et back-officeRègles de dépendance
Section intitulée « Règles de dépendance »src/lib/domain contient les modèles et interfaces stables. Il n’importe ni Svelte, ni SvelteKit,
ni infrastructure. Une règle qui peut être testée sans navigateur ni HTTP appartient ici.
Application
Section intitulée « Application »Un cas d’usage dépend de ports du domaine et expose une méthode execute(). Il orchestre une action
mais ne connaît pas fetch, les cookies ou les composants.
Infrastructure
Section intitulée « Infrastructure »Les repositories HTTP implémentent les ports, appellent HttpClient et convertissent le JSON en
entités. Les types qui reproduisent le snake_case du backend restent dans cette couche.
Présentation
Section intitulée « Présentation »Les view models formatent dates, devises, labels et états d’affichage. Un composant reçoit des props prêtes à rendre. Une route compose la page ; elle ne devient pas un service métier.
Conteneur
Section intitulée « Conteneur »src/lib/container.ts est le seul point d’assemblage. Les lectures publiques peuvent utiliser des
singletons sans identité. Les fonctions create<Feature>UseCases(token) créent un client authentifié
par requête : ne transformez jamais ces usages en singleton.
Choisir le bon mécanisme SvelteKit
Section intitulée « Choisir le bon mécanisme SvelteKit »| Besoin | Mécanisme |
|---|---|
| Charger des données avec secret ou token | +page.server.ts |
| Mutation de formulaire | form action dans +page.server.ts |
| Flux SSE, upload ou binaire | route +server.ts sous /api/back-office |
| Données publiques sans secret | load serveur par défaut |
| État visuel local | runes dans le composant ou classe .svelte.ts |
Utilisez use:enhance pour améliorer une form action, pas pour déplacer sa validation dans le
navigateur. Une page fonctionnelle sans JavaScript doit rester possible lorsque le composant s’y
prête.
Exemple d’une nouvelle fonctionnalité
Section intitulée « Exemple d’une nouvelle fonctionnalité »Pour ajouter la lecture d’un objet Amenity :
- créez
domain/amenity/Amenity.tsetIAmenityRepository.ts; - créez
application/amenity/GetAmenitiesUseCase.ts; - implémentez
infrastructure/amenity/AmenityHttpRepository.tset les types JSON ; - câblez le repository dans
container.ts; - créez
presentation/amenity/AmenityViewModel.ts; - chargez le cas d’usage dans
+page.server.ts; - rendez le view model dans un composant testé et documenté par une story.
Les tests du domaine et du cas d’usage utilisent un port factice simple. Ils ne mockent pas
HttpClient.
Site public et back-office
Section intitulée « Site public et back-office »Les deux surfaces partagent le domaine, l’application et l’infrastructure, mais pas leur système de présentation.
| Règle | Site public | Back-office |
|---|---|---|
| Routes | routes/(app) |
routes/back-office |
| UI | composants presentation/shared |
shadcn-svelte components/ui |
| Langues | cinq catalogues Paraglide | français inline |
| Thème | tokens publics @theme |
tokens sous [data-back-office] |
| Indexation | publique | noindex,nofollow |
N’importez pas les primitives shadcn dans le site public et n’utilisez pas les composants partagés
publics dans le back-office. Les portails (Dialog, Popover, Sheet, menus et toasts) doivent
porter data-back-office pour conserver le thème administratif.
Authentification et autorisation
Section intitulée « Authentification et autorisation »hooks.server.ts lit le cookie harmony_backoffice_session, valide le token auprès du backend et
remplit event.locals. Chaque load, action et endpoint vérifie ensuite lui-même la session. Les
actions sensibles contrôlent le rôle côté serveur, même si le menu est masqué.
Les cookies de session et de challenge sont httpOnly, sameSite=lax et secure hors mode dev.
Le challenge TOTP expire après cinq minutes ; une session mémorisée reçoit au maximum sept jours.
Consultez le guide Sécurité et authentification avant de modifier ce flux.
Erreurs et concurrence
Section intitulée « Erreurs et concurrence »Les erreurs HTTP sont traduites dans un helper serveur par domaine. Une indisponibilité critique
utilise error(). Une page qui peut rester utile en mode dégradé renvoie un view model vide et un
indicateur loadError.
Une mutation d’un enregistrement existant transmet expectedUpdatedAt. Un 409 demande à
l’utilisateur de recharger ; il ne faut pas écraser silencieusement la version concurrente.
Les actions multiples renvoient un payload discriminé avec action, success et un message
français prêt à afficher dans une zone live.
Svelte 5
Section intitulée « Svelte 5 »- Déclarez les props avec
$props()et les valeurs calculées avec$derived. - Réservez
$effectaux effets de bord. - Utilisez
SvelteSetetSvelteMappour les collections réactives. - Lisez la route avec
$app/state, pas les stores dépréciés. - Protégez
localStoragederrièreonMountpour préserver le SSR. - Extrayez un état client complexe dans une classe
.svelte.tstestable.
Internationalisation
Section intitulée « Internationalisation »Les chaînes publiques vivent dans messages/en.json, fr.json, it.json, de.json et es.json.
Ajoutez la même clé partout, puis exécutez :
npm run i18n:compileNe modifiez jamais src/lib/paraglide à la main. Le middleware Paraglide applique la stratégie URL,
cookie puis locale de base et renseigne les attributs lang et dir du document. Une URL publique
sans préfixe désigne le français : un cookie anglais ne la redirige pas vers /en/. Le serveur
aligne le cookie de préférence sur la variante visitée. Le sélecteur conserve le chemin, la query
et le fragment pour accéder à la page équivalente.
Les pages utilisent data.locale, fourni par le layout serveur, pour leurs textes et calendriers.
getLocale() peut servir de repli ; évitez getLocaleForUrl() pendant le SSR, car il ne consulte
pas le contexte de requête serveur et peut retourner la langue de base avant hydratation.
Le back-office est en français inline, sauf le système de notifications partagé qui utilise des clés
backoffice_* dans tous les catalogues.
Ajouter une entité à la traduction assistée
Section intitulée « Ajouter une entité à la traduction assistée »Le navigateur appelle uniquement les routes BFF authentifiées /api/back-office/ai/**. Les éditeurs
ne connaissent ni le fournisseur ni ses identifiants et une proposition ne modifie jamais le
brouillon avant une action explicite.
Pour enregistrer un nouveau type de contenu :
- ajoutez le type d’entité et ses champs autorisés au contrat partagé
domain/ai/AITranslation.ts, en accord avec le registre du backend ; - déclarez les libellés, formats, champs requis et longueurs maximales dans
AITranslationEditorViewModel.ts; - écrivez deux fonctions pures : une pour convertir le brouillon en dictionnaire de champs, une pour réappliquer partiellement ce dictionnaire sans toucher au reste de l’état ;
- affichez
AITranslationEditor.svelteuniquement lorsque la locale active diffère de la locale source activée, puis passez-lui les valeurs non enregistrées et la fonction d’application locale ; - ajoutez un test de mapping, un test d’intégration de l’éditeur et un parcours Playwright avec une réponse BFF déterministe.
Le composant commun gère le chargement, l’annulation, les erreurs, la détection d’une source devenue
obsolète, la protection des contenus cibles existants et le signalement des résultats applied ou
rejected. L’enregistrement et la publication restent les actions de formulaire existantes.
CSS, accessibilité et composants
Section intitulée « CSS, accessibilité et composants »- Utilisez les tokens CSS, jamais une couleur hexadécimale dans un composant.
- Respectez WCAG 2.1 AA : nom accessible, focus logique, contraste, état non transmis par la couleur.
- Masquez les icônes décoratives avec
aria-hidden="true". - Associez chaque champ à un label.
- Utilisez les variantes des primitives avant d’ajouter des classes ad hoc.
- Ajoutez une story CSF 3 à chaque composant présentatif réutilisable.
Validation ciblée
Section intitulée « Validation ciblée »npm run checknpm run test -- chemin/du/test.test.tsnpm run lintnpm run buildnpm run storybookAvant livraison, make verify dans web/ exécute la validation de l’application puis
docs:check. Ajoutez Playwright lorsque le comportement dépend de la navigation, du SSR, des cookies
ou de plusieurs écrans.