3.0 KiB
3.0 KiB
NFR Design Patterns — Unit 1: Project Scaffold & Infrastructure
1. Performance — Route-Based Code Splitting (Answer Q1-A)
Pattern: Per-feature route modules with lazy-loaded layouts and page components.
- TanStack Router
createLazyFileRoutefor dashboard, users, cms routes - Auth and root shell remain eager for fast initial paint and login
- Vite automatically emits separate chunks per lazy route
- Aligns with NFR-U1-01 (smallest possible bundle) and BR-U1-05 (TanStack Router)
2. Accessibility — Keyboard & Focus Trap (Answer Q2-A)
Pattern: Browser-native Tab order + shadcn/ui focus-visible + modal focus trap only.
- All interactive elements use native
<button>,<a>, form controls focus-visiblering provided by shadcn/ui theme (Tailwind ring-2 ring-offset-2)FocusTrapcomponent (from@radix-ui/react-focus-trapor shadcn/ui dialog primitive) applied exclusively to modals and confirmation dialogs- No custom roving tabindex or arrow-key navigation unless explicitly required later
3. Testing — Feature-Scoped MSW Handlers (Answer Q3-B)
Pattern: Feature folders under src/mocks/ with barrel exports.
src/mocks/
auth/
handlers.ts
fixtures.ts
users/
handlers.ts
index.ts
auth/handlers.tsexportsauthHandlersarray used in login, refresh, 401-retry tests- Easy to extend per future feature without central file bloat
- Supports NFR-U1-04 (>70% coverage on auth components)
4. Internationalization — Lazy Language Loading (Answer Q4-B)
Pattern: Dynamic import of active locale only.
i18nextinitialized withreact-i18nextandi18next-browser-languagedetector- On language change:
i18next.changeLanguage(lng)triggersimport(../../public/locales/${lng}/translation.json) - Fallback to English; no eager bundling of both languages
- Language switcher in user menu triggers the dynamic load + persists choice in localStorage via detector
5. Error Handling — Per-Component ProblemDetails Mapping (Answer Q5-B)
Pattern: Context-aware error presentation instead of global interceptor.
ApiClientthrows typedProblemDetailsError(extends Error)- Each page/form catches and decides:
- 401/403 → redirect or global AuthContext logout
- Validation errors (400) → inline field errors via react-hook-form
- Transient errors → shadcn/ui toast via
useToast()
- Keeps UI responsive and avoids one-size-fits-all toasts
6. Configuration — Typed Vite Env + Optional Zod (Answer Q6-A)
Pattern: Strong typing in vite-env.d.ts + runtime validation hook.
// vite-env.d.ts
interface ImportMetaEnv {
readonly VITE_API_BASE_URL: string;
// future flags...
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
- Optional
useAppConfig()hook runs Zod parse once at bootstrap (development warning only) - Production trusts
.envvalues (no runtime overhead)
All patterns respect the "minimal viable" constraints from NFR Requirements (no Sentry, no pre-commit, basic a11y).