# 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 ```text 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 ```text 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: ```sh 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.