La Frond

Une Frond est un module métier qui regroupe les éléments responsables d'un même domaine.

La frontière de propriété

Une Frond contient :

  • ses entités et schémas,
  • ses opérations — commands et queries (handlers),
  • ses policies — les règles d'accès vérifiées dans les opérations,
  • ses events — les faits que ses commands produisent (un bus in-process, auquel rien ne s'abonne par défaut),
  • ses adapters de persistance — comment ses entités se réalisent,
  • ses contrats publics — ce que les autres Fronds peuvent appeler,
  • ses règles d'invalidation — ce qu'une command revalide.

Une Frond n'importe pas les fichiers internes d'une autre. Les interactions passent par des contrats explicites, de préférence des opérations ou des events. Une App compose les Fronds et conserve la logique métier dans ces modules plutôt que dans ses pages, fetchs ou stores.

Les six couches

Le modèle suit six étapes :

#CoucheCe qui s'y passe
1Déclarationl'auteur déclare entités, vues, opérations, events — ni HTTP ni SQL ici
2Dérivationvalidation, types, vues io, metadata lisible par les adapters en dérivent
3Capacité localela déclaration devient exécutable in-process — le mode de référence
4Projectiondes façades sont générées vers les transports : HTTP, GraphQL, CLI, workers
5Compositionles Fronds sont scannées et reliées dans un runtime — module → contrat → interaction
6Distributionune capacité part hors process ; sérialisation, timeouts, retries apparaissent ici, explicitement

La page sur le gradient décrit le passage de l'exécution locale à l'exécution distante.

Généré vs écrit à la main

La répartition est la suivante :

Fougere génèreVous écrivez
validation, vues dérivées, tables/metadatala logique métier non triviale
types et inputs GraphQL, CRUD trivialles invariants riches, les workflows multi-étapes
les façades de projection (contrat → transport)les politiques d'erreur, les décisions de cohérence
les clients typés, les manifests de compositionl'orchestration inter-Fronds sensible

Fougere prend en charge les intégrations répétitives autour d'un contrat. Le développeur écrit le comportement propre à l'application. Une couche service reste optionnelle : un handler peut contenir la logique ou la déléguer.

Dans le système de fichiers

fronds/blog/
  entities/             ← les déclarations
  handlers/             ← les opérations   (handlers/<surface>/ = une audience nommée)
  presenters/           ← les champs calculés ajoutés à la sortie d'une entité
  collectors/           ← la résolution de paramètres par type
  services/             ← des classes ordinaires, injectées par type
  repositories/         ← la même chose, sous un second nom
  seeds/                ← les données créées au démarrage

Le scanner reconnaît une Frond à ces dossiers. La racine du projet peut suivre la même convention : une app avec un seul domaine n'a alors pas besoin de répertoire fronds/ (la forme à plat). Un deuxième domaine peut ensuite être ajouté sous fronds/ sans déplacer le premier.

Nommer et importer une Frond

Le nom du dossier est le nom de la Frond, et @fougere/nuxt en fait un alias : fronds/blog/ donne @frond/blog, donc une page écrit import Post from '@frond/blog/entities/Post' sans aucun fichier à ajouter. Ce nom sert aussi de clé aux enregistrements d'entités et à toute entrée remotes:.

Un package.json dans la Frond répond aux deux questions que le dossier ne sait pas dire.

{
  "name": "@frond/blog",
  "fougere": { "frond": "blog" },
  "exports": { "./entities/*": "./entities/*.ts" }
}

fougere.frond permet d'utiliser un nom différent de celui du dossier. Par exemple, fronds/blog-v2/ peut rester enregistrée sous le nom blog, ce qui conserve ses clés d'entité, ses imports @frond/* et ses entrées remotes:. Sans cette propriété, Fougere utilise le nom du dossier.

name et exports rendent la Frond résolvable en dehors d'une app Nuxt — un script Node, un consommateur non-Nuxt — ce qui demande aussi de la déclarer comme paquet du workspace (packages: [fronds/*] dans pnpm-workspace.yaml). Dans une app Nuxt, l'alias suffit.

services/ et repositories/ se comportent à l'identique : les deux enregistrent leurs classes comme providers, résolus par type. La plupart des accès data n'ont besoin ni de l'un ni de l'autre — le handler reçoit déjà un EntityOrm scopé à son entité. On prend un provider quand la lecture ne rentre pas dans cette forme : un agrégat sur plusieurs entités, une source qui n'est pas un ORM (une API externe, un index de recherche), ou une requête assez lourde pour que le handler n'ait pas à la porter.

Construit avec Fougere — ce site tourne sur le framework qu'il documente.