Spaces:
Running
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:
- Call a feature API facade or shared discovery hook, never
fetch()from a UI component. - Define a stable query key.
- Invalidate only the keys affected by a successful mutation.
- Use optimistic updates only with a rollback snapshot.
- 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.tsdefines a generic subscription and React hook. Jobs adapt their existing WebSocket/SSE/polling fallback to it throughfeatures/jobs/services/realtime.ts; other features can use the same contract.features/media/services/upload-service.tsis the single feature-level entry point to the existing chunked upload protocol. It supports cancellation viaAbortController; transport/protocol discovery remains inlib/api.features/searchmaps real template, operation and MCP discovery metadata to internal routes. It has no hard-coded backend payloads or client-provided URLs.features/editor/index.tssupplies 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
- Preserve the route URL and add/refine its feature boundary first.
- Put requests in a feature
api/facade that reuseslib/api/client.ts. - Put server data in TanStack Query, browser-only local interaction in a narrowly scoped store, and domain algorithms in feature services/utils.
- Register capability-driven navigation, commands or plugin metadata instead of editing the shell directly.
- Reuse the Design System and shared error/realtime/upload primitives.
- 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.