MediaRouter / docs /frontend-architecture-v2.md
basyx's picture
Upload 794 files
1b2323a verified
|
Raw
History Blame Contribute Delete
11.5 kB

MediaRouter Frontend Architecture v2

Purpose

Frontend Architecture v2 is the incremental, feature-oriented foundation for MediaRouter. It preserves the existing Next.js App Router URLs, authentication and backend contracts while making the current product areas and future AI-first workspace modules independently maintainable.

It is intentionally an architecture migration, not a visual redesign or a new product surface. The MediaRouter Design System remains the sole UI primitive layer; features compose it and do not recreate buttons, forms, navigation or feedback elements.

Layering

app routes (thin route and metadata adapters)
        |
feature boundary (screen, hooks, schemas, local state, feature API facade)
        |
shared application services (auth, query, flags, capabilities, realtime)
        |
lib/api/client.ts -> authenticated same-origin BFF -> MediaRouter backend

All browser requests use the shared Axios client at frontend/lib/api/client.ts, whose base URL is the same-origin /api/backend bridge. UI components must not call fetch() directly. app/api/backend/[...path]/route.ts remains the only frontend transport proxy. It owns authentication forwarding, backend authorization and safe request forwarding; no frontend API contract changed in this migration.

Directory structure

frontend/
  app/
    (auth)/                 authentication routes
    (platform)/             preserved workspace URLs during incremental migration
    (public)/               public/legal routes
    api/                    BFF and authentication route handlers
  components/
    ui/ layout/ navigation/ feedback/  design-system consumers only
  design-system/            tokens, themes, icons, motion and foundations
  features/
    auth/ workspace/ templates/ social/ jobs/ media/ developer/
    dashboard/ settings/ operations/ marketplace/ mcp/
    search/ commands/ plugins/ editor/
  lib/
    api/ auth/ query/ capabilities/ feature-flags/ realtime/ config/
  hooks/                    cross-feature compatibility or presentation hooks

The existing (platform) route group intentionally remains in place. Route groups do not affect URLs, so /dashboard, /templates, /social, /developer/*, and every existing bookmark retain their current address. New workspace routes should be introduced below the agreed (workspace) group only when their corresponding product module is implemented; no duplicate or mock pages are created by this foundation.

Feature boundary

Each feature is the owner of its screen-level components, feature hooks, schemas/types, local stores, services and an api/ facade. The facade imports the shared transport modules from lib/api; it does not instantiate a second HTTP client. This keeps authentication, error handling, timeout and BFF policy consistent while allowing a feature to control its public data interface.

Current active feature boundaries include:

  • auth: session hook and protected route integration.
  • workspace: discovery bootstrap, shared discovery queries, navigation and workspace chrome state.
  • templates: browser, detail, runner, preferences store and template API facade.
  • social: social screens, query/mutation hooks and social API facade.
  • jobs: job monitor and reusable job realtime adapter.
  • media: media input, transfer state and upload service.
  • developer, settings, operations, mcp, marketplace, dashboard: their existing screens behind explicit feature entry points/API facades.
  • search, commands, plugins, editor: reusable infrastructure with no new product UI or mock data.

Foundational feature declarations live in features/manifest.ts. They let tooling and navigation identify active, foundation and planned product areas without turning planned functionality into visible routes.

State management

State is deliberately split by lifecycle and ownership:

State Location Rule
Global workspace chrome features/workspace/stores/workspace-ui-store.ts sidebar and command-palette visibility only
User template preferences features/templates/stores/template-preferences-store.ts browser-local persisted preferences only
Transfers features/media/stores/transfer-store.ts active browser transfer state only
Server data feature TanStack Query hooks never copied into Zustand
Editor state features/editor/index.ts one vanilla Zustand store per editor instance

Authentication remains session-backed through NextAuth and is exposed through features/auth/hooks/use-auth.ts. Workspace/project/server data remains authoritative on the backend. Do not create a catch-all Zustand store.

API and TanStack Query policy

lib/api/errors.ts converts transport failures into a safe MediaRouterApiError and normalized categories: authentication, authorization, validation, not-found, rate-limited, offline, timeout, server and unknown.

lib/query/client.ts configures one QueryClient with bounded retry/backoff for retryable transport failures only. Query keys are stable and feature-owned; shared cross-feature keys live in lib/query/keys.ts. Features must:

  1. Call a feature API facade or shared discovery hook, never fetch() from a UI component.
  2. Define a stable query key.
  3. Invalidate only the keys affected by a successful mutation.
  4. Use optimistic updates only with a rollback snapshot.
  5. Surface normalizeApiError() through the feedback states rather than raw server payloads.

The shared feedback layer provides API, validation, permission and offline error-state building blocks, plus retry banners. Backend error details and request IDs stay safe to render, but credentials and private upstream targets never enter the browser.

Server and client component strategy

Route pages, metadata and authorization checks remain Server Components by default. A component is marked client-side only when it needs browser state, TanStack Query, event handlers, uploads, realtime, drag-and-drop, command palette interaction, a browser API or animation.

Examples of client-only infrastructure are the app shell, global search, upload service consumers, job subscriptions, forms and editor stores. New features should keep route adapters thin, use Suspense boundaries around client data islands, and avoid pulling heavy editor/timeline modules into the workspace shell.

Permissions, capabilities and feature flags

The backend remains authoritative. lib/auth/permissions.ts provides the frontend UX check and the BFF separately checks every forwarded request.

lib/capabilities/index.ts evaluates a declarative requirement containing an optional backend capability, permission and feature flag. Navigation, commands and plugins use this same resolver. Do not hardcode role checks in components.

lib/feature-flags/index.ts is the only environment-backed flag resolver. useFeatureFlags() supplies the UI layer, supports future workspace, organization and user overrides, and keeps pages from reading environment variables directly. A backend-provided override can be passed as scoped metadata when the backend exposes it.

Navigation

features/workspace/navigation/registry.ts declares workspace, developer and footer navigation. useWorkspaceNavigation() combines it with the authenticated subject, backend OpenAPI discovery, feature flags and plugin registrations. AppSidebar only renders resolved entries, so it contains no route-specific permission policy. Dynamic badges may be supplied in a future navigation registration without changing the shell.

Commands and plugins

features/commands/registry.ts is an empty-by-default command-palette registry. A command has a group, shortcut, capability requirement and an optional lazy load() function. Infrastructure exists; this migration does not populate product commands.

features/plugins/registry.ts is a metadata registry for future modules. Plugins can register routes, navigation, commands, settings and capability requirements. Plugins cannot bypass route authorization, feature flags, capability checks or the shared API client. The registry does not load remote code and no marketplace plugin is enabled by this foundation.

Realtime, uploads, search and editor foundation

  • lib/realtime/subscription.ts defines a generic subscription and React hook. Jobs adapt their existing WebSocket/SSE/polling fallback to it through features/jobs/services/realtime.ts; other features can use the same contract.
  • features/media/services/upload-service.ts is the single feature-level entry point to the existing chunked upload protocol. It supports cancellation via AbortController; transport/protocol discovery remains in lib/api.
  • features/search maps real template, operation and MCP discovery metadata to internal routes. It has no hard-coded backend payloads or client-provided URLs.
  • features/editor/index.ts supplies isolated selection/history/undo/redo and plugin state. It intentionally does not implement an editor surface, collaboration protocol or timeline UI.

Performance and accessibility

The refactor keeps the workspace shell small, centralizes QueryClient policy, uses stable query keys and makes command/plugin work lazy-loadable. Heavy future modules (editor, timeline, media inspection, collaboration) must be loaded behind feature boundaries and Suspense rather than imported by the shell. Existing image handling stays under Next.js configuration.

Architecture changes preserve the Design System’s WCAG AA baseline: semantic tokens, keyboard-capable Radix primitives, labelled controls, focus management, reduced motion and responsive workspace shell behavior. New capability checks hide unavailable UI but do not substitute for backend authorization.

Testing

Vitest tests are colocated with the behavior they protect. Shared fixtures and setup remain centralized in frontend/vitest.setup.ts. The architecture suite covers feature flag resolution, capability evaluation, navigation filtering, command and plugin registration, editor history, safe search mapping and API error classification. Existing component/auth/API tests remain unchanged.

Run locally:

cd frontend
npm run typecheck
npm run lint
npm test

The current Android/Termux environment cannot complete native Turbopack and Storybook production builds because their native bindings are unavailable. This is an environment limitation, not a reason to bypass typecheck, lint or unit tests.

Migration and contribution guide

  1. Preserve the route URL and add/refine its feature boundary first.
  2. Put requests in a feature api/ facade that reuses lib/api/client.ts.
  3. Put server data in TanStack Query, browser-only local interaction in a narrowly scoped store, and domain algorithms in feature services/utils.
  4. Register capability-driven navigation, commands or plugin metadata instead of editing the shell directly.
  5. Reuse the Design System and shared error/realtime/upload primitives.
  6. Add focused tests, then run typecheck, lint and the full unit suite.

Do not move existing routes solely for directory aesthetics, duplicate API clients/hooks, read environment variables in a product page, create mock data or encode backend authorization in a UI-only permission check. The migration is backward compatible by design; there are no public route, API or authentication breaking changes in v2.