Skip to content

Latest commit

 

History

History
274 lines (198 loc) · 11.4 KB

File metadata and controls

274 lines (198 loc) · 11.4 KB

BFF (Backend for Frontend)

The Platform BFF layer exposes a renderer-agnostic UI contract (bff.v1). Every user-facing surface must be renderable from a BFF model. Renderers do not own business rules, persistence, permissions, or resource-specific mutations.

Default architecture is BFF contract + renderer adapter, not a specific frontend stack. Templ, React/Vite, Next, Vue, MPA, SSR, SSG, and islands are adapter choices; Platform core stays stack-neutral.

Invariant

module -> panel/toolset -> BFF runtime -> PageModel -> renderer

BFF owns permissions, validation, navigation, session projection, action descriptors, and hydrated data shape before any renderer runs. Renderers map the model to HTML or client UI only.

Two deliveries of the same model:

Delivery Route Consumer
In-process HTML GET /go-admin/{path...} Templ reference renderer (bff.PageModel Go structs)
HTTP JSON GET /bff/screens/{path...} React/Vite, Next, Vue, PHP, mobile, automation clients

Templ is the primary reference renderer, not a privileged path: it must not bypass BFF for capability checks, validation, action paths, or alternate screen shape. Vite, Next, Vue, PHP, and other clients consume the same bff.v1 fields over /bff/*.

Data hydration vs UI hydration

Use hydration precisely:

Kind Owner Meaning
Data/model hydration BFF (Hydrator) Fill ScreenModel.Rows, Pagination, FormRecord, validation state, action metadata
UI hydration Renderer adapter React hydrate, islands boot, partial client behavior, SSG enhancement

BFF owns model hydration only. UI hydration belongs to renderer adapters, not pkg/bff.

Surface model

BFF is not admin-only. The same bff.v1 contract applies across surfaces; profiles and product apps choose which surfaces are mounted and which route bases they use.

Surface Purpose Example path (product-owned)
admin Operator / control-plane UI /go-admin/posts, /go-admin/crm/leads
public Public-facing pages /, /posts/{slug} (AppCMS public)
portal Authenticated external-user UI /portal/account, CRM client dashboard
embedded Widget or iframe-like UI status widget, lead capture form
mobile JSON-first app model mobile task list via /bff/screens/...
automation CLI, LLM, bot clients action catalog, screen data, validation envelopes

Rules:

  1. Platform defines the shared model contract (PageModel, ScreenModel, actions).
  2. Profiles own route bases (AdminBase, APIBase, PublicBase in profile.Profile).
  3. Product apps/modules own surface policy, hydration, executors, and special screens.
  4. Public and portal are not CMS-only; CRM, monitoring, support, and SaaS apps use the same pattern.
  5. Mobile and automation clients may consume BFF JSON without a browser renderer.

Optional ScreenModel.metadata may carry surface hints (surface, visibility, list_api, form_action). Platform does not hardcode public/portal domain behavior.

See API surfaces for REST vs BFF vs HTML routing.

PageModel

PageModel is the unified entry contract for user-facing UI:

Part Type Role
title string Page title
shell ShellModel Product chrome: title, workspace, admin/api bases
navigation NavigationModel Capability-filtered nav items
screen render.ScreenModel View type, columns, fields, rows, actions, metadata

ShellModel fields: title, product, workspace, admin_base, api_base, description.

NavigationModel contains items[] with href, label, and optional capability.

SessionModel (via /bff/session) exposes a safe subset: contract version, authenticated state, principal_id, profile_id, and sorted capability ids. No secrets or tokens.

HTTP Endpoints (Stage 4–6)

Product apps mount these routes (AppCMS, AppCRM):

GET /bff/screens/{path...}   -> ScreenModel (hydrated when hydrator is active)
GET /bff/nav                 -> NavigationModel
GET /bff/session             -> SessionModel
POST /bff/actions/{actionId} -> ActionResult | ValidationModel (Stage 6, AppCMS posts proof)

Path encoding: logical screen path without leading slash in the URL segment (for example go-admin/posts, or a product-defined public/portal path when wired).

Surface Example screen BFF JSON path
Admin (proof) AppCMS posts list GET /bff/screens/go-admin/posts
Admin (proof) AppCRM leads list GET /bff/screens/go-admin/crm/leads
Public / portal / embedded Product-defined GET /bff/screens/{product-path...} when mounted

Auth semantics

  • Unauthenticated: 401 JSON ({"error":"unauthorized"}), not a login redirect.
  • Missing admin/CRM capability: 403 JSON ({"error":"forbidden"}).
  • Same principal resolution as /go-admin/* (session cookie, AppCMS Bearer token).

Parity rule

For proof screens, the JSON body of /bff/screens/... must match the ScreenModel produced in-process by registry.Page(...) — the same model Templ renders. Contract tests in AppCMS and AppCRM enforce this.

Platform package

github.com/fastygo/platform/pkg/bff:

Component Role
Resolver Path -> ScreenModel from bindings + special screens
ScreenHydrator Product-owned data load into rows, pagination, form values
Navigator Capability-filtered navigation from panel resources
PageRuntime Composes resolver + hydrator + navigator into PageModel
ActionRegistry / ExecutorRegistry Descriptor registry + product dispatch
ActionResult / ValidationModel Mutation response envelope

Contract version: bff.v1 (bff.Version).

For bff.v1, render.ScreenModel is the screen ABI only at the renderer-visible JSON boundary. The frozen contract is the combination of JSON Schema, TypeScript types, and golden fixtures. In particular, panel.Column and panel.Field payloads keep their Go-derived PascalCase keys (ID, Label, Options, Relation, ...). Any future DTO split must preserve v1 JSON compatibility or ship as bff.v2.

Public API freeze: see Compatibility policy and schema/bff/v1/README.md.

Product app wiring

Apps keep a thin internal/appschema registry that:

  • builds bff.PageRuntime from module descriptors;
  • registers product-specific special screens (settings, kanban, dashboard);
  • exposes Page(ctx, path, principal) and DashboardPage(ctx, principal).

pkg/app admin HTML routes call registry.Page and render Templ. pkg/app/bff.go exposes the same runtime over JSON.

React/Vite proof renderer (Stage 5)

web/react is a compatibility proof, not the default Platform renderer. It demonstrates JSON BFF parity with the Templ reference renderer for proof screens. See web/react/README.md for dev setup (Vite proxy -> Go /bff/*).

Generic actions (Stage 6)

POST /bff/actions/{actionId} dispatches by ActionDescriptor metadata. Product apps register executors; Platform owns contract types only.

AppCMS proof actions:

Action Path Capability Token scope
Create post POST /bff/actions/post.create content.write admin.content.write
Update post POST /bff/actions/post.update?id={id} content.write admin.content.write
Trash post POST /bff/actions/post.trash?id={id} content.write admin.content.write

Responses:

  • 200 + ActionResult (ok, optional redirect, data)
  • 422 + ValidationModel for invalid input
  • 401 / 403 for auth, capability, or token failures
  • 404 for unknown action ids

Post form screens set metadata.form_action to the BFF action path and metadata.action_scope for scoped action_token injection. Codex REST (/go-json/*) remains unchanged for integrations.

Dashboard and widget models (F5)

Dashboard screens use view: "dashboard" with optional sections[] and widgets[] on ScreenModel. Platform promotes only shared primitives; product domain stays in metadata until reuse is proven.

Primitive JSON field Role
SectionModel sections[] Layout container with titled widget grid
WidgetModel sections[].widgets[] or widgets[] Typed widget slot
MetricModel widget.metric KPI tile (label, value, optional unit, trend)
StatusModel widget.status Health/state badge (status, message)
ChartModel widget.chart Chart summary (chart_type, series[])
ActivityFeedModel widget.activity Timeline/incident list

Widget types: metric, status, chart, activity.

Proof dashboard fixtures (no Platform import of product modules):

Fixture Product flavor
monitoring-sites-dashboard.json Monitoring sites fleet overview
appcrm-pipeline-dashboard.json CRM pipeline metrics
appcms-content-health-dashboard.json CMS content health

Templ (DashboardScreen) and React (DashboardView) render metric and status widgets from these fixtures. Platform must not import monitoring/CMS/CRM app packages to prove the contract.

Renderer parity fixtures (Stage 7)

Canonical proof-screen JSON lives in pkg/conformance/testdata/bff/v1/:

  • appcms-posts-table.json
  • appcrm-leads-table.json
  • appcms-post-edit-form.json

pkg/conformance/bffparity loads fixtures, normalizes volatile metadata (action_token is stripped), and asserts strict ScreenModel roundtrip stability. AppCMS and AppCRM HTTP tests compare /bff/screens/* output to the same fixtures.

Run the regression gate:

./scripts/bff-parity.sh

CI workflow: .github/workflows/bff-parity.yml.

Renderer ecosystem artifacts (Stage 8)

Stable renderer-facing exports for the bff.v1 baseline:

Artifact Path
Go page/shell/nav/session pkg/bff
Go screen model pkg/render
JSON Schema schema/bff/v1/ (screen, page, navigation, session, actions)
TypeScript contract web/contract (@fastygo/bff-contract)
Golden fixtures pkg/conformance/testdata/bff/v1/*.json
Adapter proofs examples/renderer-adapters

Proof screens: AppCMS posts table, AppCMS post edit form, AppCRM leads table.

Go conformance validates golden fixtures against screen.schema.json (pkg/conformance/schema_test.go). Third-party renderers can implement proof screens from schema + fixtures alone. See renderer-adapter-authoring.md.

What BFF is not

  • Not codex REST — use /go-json/* for integrations and headless data.
  • Not a renderer — adapters map PageModel to HTML or client UI only.

Related documents

  • API surfaces/go-json/* vs /bff/* vs /go-admin/*
  • Frontend adapters — renderer adapter guide (Vite/Next examples)
  • Custom screens — app/module-owned views
  • Renderers — renderer-agnostic runtime and parity
  • Templ renderer — reference in-process renderer
  • Contract spec: .project/specs/bff-v1-screen-schema.md
  • Strategic roadmap: .project/roadmab-fullstack-bff.md
  • Implementation roadmap: .project/roadmap-bff-model.md