Type to search pages and rules.

Browse pages

Frontend Structure

A frontend holds three parts: a frame, routes, and use-case modules.

Markdown13 provisions

Intent#

A frontend holds three parts: a frame, routes, and use-case modules. The frame holds everything cross-cutting. The routes hold the addresses the frontend serves. The modules hold one folder per backend use case the frontend calls. Each frontend is one entry-point record.

The module tree mirrors the backend Application tree in kebab case. A reader holding a use case specification then finds the frontend code that serves it without a second map.

Standards#

Organize a frontend into frame, routes, and modules#

ruleguidance

Requirement A frontend MUST organize its code into a frame, routes, and use-case modules.

Rationale The frame holds everything cross-cutting. The routes hold the addresses the frontend serves. The modules hold one folder per backend use case the frontend calls.

Example Each stack names these three parts for its own toolchain.

Text
{frontend}/
  frame/     client configuration, session, language, permissions, formatting, shell data
  routes/    one entry per address
  modules/   one folder per backend module, aggregate, and use case
litespec/frontend/structure#organize-a-frontend-into-frame-routes-and-modules

Mirror the backend use case path in kebab case#

ruleguidance

Requirement A use-case folder MUST sit under the modules root at {module}/{aggregate}/{use-case}/ in kebab case, resolving to the backend folder of the same three names.

Rationale A reader holding a specification page then finds the frontend code by translating three names. The reader learns no second register to make that translation.

Example One backend folder and its frontend counterpart are the same path in two spellings.

Text
apps/api/src/Shop.Application/Orders/Orders/SearchMerchantOrders/
{frontend}/modules/orders/orders/search-merchant-orders/

For example, the library product borrows a copy through use-case/lending.borrow-copy, implemented at apps/api/src/Library.Application/Lending/Loans/BorrowCopy/. The catalog frontend serves it from the same path in kebab case.

Text
apps/api/src/Library.Application/Lending/Loans/BorrowCopy/
apps/catalog/src/modules/lending/loans/borrow-copy/

The three backend names become three lowercase folder names, so a reader holding the use case finds the frontend code by translating Lending, Loans, and BorrowCopy.

The diagram shows the backend path beside its frontend mirror, with one arrow per translated segment.

The backend use-case path beside its frontend mirror Three backend segments, Lending, Loans, and BorrowCopy, each with an arrow to its frontend spelling: lending, loans, and borrow-copy. The backend path is Library.Application/Lending/Loans/BorrowCopy and the frontend path is modules/lending/loans/borrow-copy. Backend: Library.Application (PascalCase) Frontend: modules (kebab-case) module aggregate use case Lending Loans BorrowCopy lending loans borrow-copy An arrow maps one backend segment to its frontend spelling.
The backend folder Lending, Loans, BorrowCopy beside the frontend folders lending, loans, borrow-copy, with one arrow mapping each backend segment to its kebab-case spelling.
litespec/frontend/structure#mirror-the-backend-use-case-path-in-kebab-case

Keep a use case folder flat#

ruleguidance

Requirement A folder named for one use case MUST hold files and no subfolder.

Rationale A subfolder inside a use case names a thing the backend does not have. The mirror stops being checkable at that point, and nothing reports it.

litespec/frontend/structure#keep-a-use-case-folder-flat

Place a shared file in the folder that owns it#

ruleguidance

Requirement A file read by several use cases of one aggregate MUST sit flat in that aggregate's folder.

Rationale The lowest folder holding every reader is the only place where moving the file changes nothing else. A reader finds it beside its consumers rather than through an index.

Example Three order queries share one tab strip, so the tab strip sits flat in the aggregate folder. A file read by several aggregates sits flat in the module folder.

litespec/frontend/structure#place-a-shared-file-in-the-folder-that-owns-it

Reject a folder name that owns nothing#

ruleguidance

Requirement A folder under src MUST NOT be named shared, common, utils, util, helpers, misc, components, hooks, types, or services.

Rationale A folder with one of these names can refuse nothing, so it collects everything and nothing in it is reviewed against a use case.

Example A shared query three use cases read sits flat in the aggregate folder, not in a shared/ folder beside them.

litespec/frontend/structure#reject-a-folder-name-that-owns-nothing

Keep a route entry thin#

ruleguidance

Requirement A route entry MUST hold its input, its load, and its component, with no reusable logic and no reference to another route entry.

Rationale A column definition or a validation rule in a route entry is unreachable from the second route that needs it. A reference between two route entries makes one address depend on another.

Example An order list route validates its search, prefetches its read, and composes one screen from module components.

litespec/frontend/structure#keep-a-route-entry-thin

Order imports from routes to shared packages#

ruleguidance

Requirement An import MUST point from a route to a module to the frame to a shared package, and never in the reverse direction.

Rationale The direction keeps addresses, use-case code, cross-route values, and shared code in separate layers.

Example This direction shows the allowed dependency path.

Text
routes -> modules -> frame -> shared packages

A module cannot import a route. A shared package cannot import an application. A frame module cannot reach into one module's use-case folder for a second module's data.

litespec/frontend/structure#order-imports-from-routes-to-shared-packages

Keep applications independent#

ruleguidance

Requirement A frontend MUST NOT import source from another frontend.

Rationale Each application owns its routes, its environment, its session handling, its words, its tests, and its deployment. A shared import moves one of those into the wrong place.

litespec/frontend/structure#keep-applications-independent

Keep shared packages non application specific#

ruleguidance

Requirement A shared package MUST NOT contain page composition, feature state, authentication policy, or application components.

Rationale It holds the generated client, the shared UI package, the language machinery, and the frontend tooling. Each serves every frontend and names none of them.

litespec/frontend/structure#keep-shared-packages-non-application-specific

Locate cross-route values in the frame#

ruleguidance

Requirement A frontend MUST place cross-route values in its frame: client configuration, session read, language choice, permissions, formatting, and shell data.

Rationale These values cross every route, so a route holding one of them holds it twice with two copies to keep equal. A browser API that a use-case module reaches belongs in the frame, because a module renders and reads data rather than touching the device.

litespec/frontend/structure#locate-cross-route-values-in-the-frame

Cover public use cases in journeys#

ruleguidance

Requirement Every public use case MUST appear in a journey or name its background caller.

Rationale A use case in no journey and with no named caller is unreachable from every actor. The journey or the caller name states who reaches it and why.

litespec/frontend/structure#cover-public-use-cases-in-journeys

Conventions#

Use explicit public entry points for shared packages#

conventionguidance

Default Expose a shared package only through its documented public entry points.

Replacement A consumer can replace this default with an explicit local convention.

Rationale An application importing an internal path couples itself to a layout the package may change.

litespec/frontend/structure#use-explicit-public-entry-points-for-shared-packages

Place tests at their ownership boundary#

conventionguidance

Default Place a logic test beside its module, and keep browser suites under one tests root per frontend.

Replacement A consumer can replace this default with an explicit local convention.

Rationale A focused test stays discoverable from the code it covers, while the browser suites share one fixture set and one deployment.

litespec/frontend/structure#place-tests-at-their-ownership-boundary

Reference example#

This informative example demonstrates litespec/frontend/structure#mirror-the-backend-use-case-path-in-kebab-case, litespec/frontend/structure#place-a-shared-file-in-the-folder-that-owns-it, and litespec/frontend/structure#reject-a-folder-name-that-owns-nothing.

A search use case folder holds its query definition and its table. The aggregate folder holds the tab strip its three sibling use cases share. Nothing sits in a shared/ folder, because that name owns nothing.

Verification#

IDMarkEvidence
litespec/frontend/structure#organize-a-frontend-into-frame-routes-and-modulesguidanceDoes each frontend use a frame, routes, and use-case modules?
litespec/frontend/structure#mirror-the-backend-use-case-path-in-kebab-caseguidanceDoes each use-case folder resolve to its backend folder?
litespec/frontend/structure#keep-a-use-case-folder-flatguidanceDoes each use-case folder hold files and no subfolder?
litespec/frontend/structure#place-a-shared-file-in-the-folder-that-owns-itguidanceDoes each shared file sit in the narrowest folder holding every reader?
litespec/frontend/structure#reject-a-folder-name-that-owns-nothingguidanceDoes any folder carry a refused name?
litespec/frontend/structure#keep-a-route-entry-thinguidanceDoes each route entry hold only its input, its load, and its component?
litespec/frontend/structure#order-imports-from-routes-to-shared-packagesguidanceDoes every import follow the route to module to frame to package direction?
litespec/frontend/structure#keep-applications-independentguidanceDoes any frontend import source from another frontend?
litespec/frontend/structure#keep-shared-packages-non-application-specificguidanceDoes any shared package name a use case or a screen?
litespec/frontend/structure#locate-cross-route-values-in-the-frameguidanceDoes the frame hold every cross-route value and browser use?
litespec/frontend/structure#cover-public-use-cases-in-journeysguidanceDoes every public use case appear in a journey or name its background caller?
litespec/frontend/structure#use-explicit-public-entry-points-for-shared-packagesguidanceDoes any application import a path its package does not export?
litespec/frontend/structure#place-tests-at-their-ownership-boundaryguidanceDo logic tests sit beside their module and browser suites share one root?