The Frond

A Frond is a business module that groups the elements responsible for one domain.

The ownership boundary

A Frond contains:

  • its entities and schemas,
  • its operations — commands and queries (handlers),
  • its policies — access rules checked inside operations,
  • its events — facts its commands produce (an in-process bus, which nothing subscribes to by default),
  • its persistence adapters — how its entities are realized,
  • its public contracts — what other Fronds may call,
  • its invalidation rules — what a command revalidates.

A Frond does not import another Frond's internal files. Interactions go through explicit contracts, preferably operations or events. An App composes Fronds and keeps business logic in those modules rather than in its pages, fetches, or stores.

The six layers

The model follows six steps:

#LayerWhat happens
1Declarationthe author declares entities, views, operations, events — no HTTP, no SQL here
2Derivationvalidation, types, io views, adapter-readable metadata derive from it
3Local capabilitythe declaration becomes executable in-process — the reference mode
4Projectionfaçades are generated toward transports: HTTP, GraphQL, CLI, workers
5CompositionFronds are scanned and linked in one runtime — module → contract → interaction
6Distributiona capability moves out of process; serialization, timeouts, retries appear here, explicitly

The gradient page covers the move from local to remote execution.

Generated vs handwritten

The split of responsibilities is:

Fougere generatesYou write
validation, derived views, tables/metadatanon-trivial business logic
GraphQL types and inputs, trivial CRUDrich invariants, multi-step workflows
projection façades (contract → transport)error policies, consistency decisions
typed clients, composition manifestssensitive inter-Frond orchestration

Fougere handles repetitive integrations around a contract. The developer writes application-specific behavior. A service layer remains optional: a handler can contain the logic or delegate it.

In the file system

fronds/blog/
  entities/             ← the declarations
  handlers/             ← the operations   (handlers/<surface>/ = a named audience)
  presenters/           ← computed fields added to an entity's output
  collectors/           ← parameter resolution by type
  services/             ← plain classes, injected by type
  repositories/         ← the same, under a second name
  seeds/                ← data created at startup

The scanner recognizes a Frond through these directories. The project root can follow the same convention, so a single-domain app does not need a fronds/ directory (the flat shape). A second domain can later be added under fronds/ without moving the first one.

Naming and importing a Frond

The directory name is the Frond's name, and @fougere/nuxt turns it into an alias: fronds/blog/ gives @frond/blog, so a page writes import Post from '@frond/blog/entities/Post' with no file to add. The name also keys the entity registrations and any remotes: entry.

A package.json in the Frond answers the two questions the directory cannot.

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

fougere.frond allows a name that differs from the directory. For example, fronds/blog-v2/ can remain registered as blog, preserving its entity keys, @frond/* imports, and remotes: entries. Without this property, Fougere uses the directory name.

name and exports make the Frond resolvable outside a Nuxt app — a plain Node script, a non-Nuxt consumer — which also needs the Frond declared as a workspace package (packages: [fronds/*] in pnpm-workspace.yaml). Inside a Nuxt app the alias already covers it.

services/ and repositories/ behave identically: both register their classes as providers, resolved by type. Most data access needs neither — the handler already gets an EntityOrm scoped to its entity. Reach for a provider when the read does not fit that shape: an aggregate spanning several entities, a source that is not an ORM at all (an external API, a search index), or a query heavy enough that the handler should not carry it.

Built with Fougere — this site runs on the framework it documents.