Philosophy
Fougere describes an application through data, operations, contracts, and domain boundaries. Adapters then connect that model to Nuxt, HTTP, SQL, caches, or a remote process.
Declarations project
An entity describes data independently of its storage. Its declaration provides the TypeScript type, validation, and metadata used by forms, SQLite, and GraphQL. When the same rule appears in several layers, it should move to the shared declaration when its scope allows it.
Operations, not field writes
A Frond exposes commands to change the system, queries to read it, and, when
useful, events produced by commands. Crud(Post) provides the five standard
operations. A business transition uses an explicit verb:
publish checks its rules before changing status, instead
of allowing the client to write that field directly.
The Frond owns
A Frond groups the entities, operations, access rules, and adapters for one domain. Interactions with another Frond go through its public contracts. The application composes these modules without moving their logic into pages, fetches, or stores.
Topology is configuration
The configuration specifies whether a Frond runs locally or on a remote host. Calls use the same API in both cases. The runner executes a local call in memory and uses the configured transport for a remote call.
The boundary also lets a Frond have its own repository and release cycle: only its
contract must travel, never its source — fougere sync reads it off the running host,
fougere build-frond turns it into an installable package
(where the code lives).
HTTP is a projection, not a foundation
Many frameworks start from a route: the first-class object is a request, and the model, the validation and the business layer hang off it. Here the first-class object is the operation — a public method that receives an invocation, and that has never heard of HTTP.
This is not a matter of style; it is what makes the rest possible. An operation that
ignores transport can be called in memory, put on a JSON-RPC wire, translated into a REST
route, a GraphQL field or a shell command — with none of those forms privileged.
@fougere/core depends on no server, and
@fougere/http has no
dependencies at all: you bring Hono, Fastify, Nitro — or nothing.
The corollary is that JavaScript takes you everywhere it goes. The declaration and its judge run in the browser as they do at the façade: the same object, not a faithful copy.
Transport is a leaf
A complete Fougere application may have no HTTP at all. Entities, operations, storage, validation, injection and seeds all work without a server ever starting. A shell command, a worker, a test, an in-memory call are not degraded versions of the application: they are whole uses of it.
That is why HTTP sits at the same rank as the other projections, not beneath them. One declaration yields the SQL table, the GraphQL type, the form contract and the route: four readings of a single statement, none of them the ground the others stand on.
Two consequences, and they are what makes the principle useful:
- were transport the foundation, you could not remove it — so no in-memory call, so no gradient;
- the surfaces could not return the same verdict, since one of them would be the floor beneath the rest.
So this is not a claim of independence from any server, nor an implementation choice. It is the rule stated at the top of this page — one declaration, many projections — applied where many frameworks put their foundation.
State is losable or durable
To choose between a cache and durable storage, consider what losing the value changes. A
value that can be recomputed from a query can remain in a cache. A value whose loss
changes behavior should be stored as business data, even when it has a short lifetime. A
one-time OAuth code can, for example, be represented as { code, userId, expiresAt }
with a destructive read.
Embedded or externalized
Elements used in several contexts ship with the class; execution details are connected by the runtime.
- Embedded — validation, input or output projections, and metadata. They can be used in the browser, at the façade, or in a remote Frond.
- Externalized — storage, transport, and rendering. These adapters can vary with the application and the surface being used.
Validation is embedded so the same rules apply in each context. Storage remains external so the application can select an adapter. Metadata does not depend on topology; its use depends on the runtime that loads it.
Design order
When making a design decision, ask these questions in order:
- What is the application concept?
- What is its ownership boundary?
- Which contracts must it expose?
- Which operations change state?
- Which queries read it?
- What invalidates what?
- Which adapter executes or projects this model?
This order avoids spreading one rule across a route, page, table, and transport.
Why “Fougere”
A fern consists of fronds that repeat a structure similar to the whole plant. The name refers to the framework's composition: fields form an entity, entities and their operations form a Frond, and an application composes several Fronds.
Position
Fougere targets data-intensive applications that use one business model across several surfaces and execution modes. Nuxt, CRUD, and transports are integrations of that model, not its definition.
Next: The Frond.