Platform ServicesApp (Next.js)
App Architecture Rules
Frontend Architecture & Codebase Conventions for Wordloop App
Architecture Rules: App Service (Next.js)
wordloop-app is a Next.js 16 App Router application. While frontend frameworks dictate specific file-system routing, we still deeply adhere to our overarching Service Architecture Principles by mapping Hexagonal concepts to our React file tree. Strict adherence to these boundaries is mandatory.
1. Core Tooling
- Package Manager: Use
pnpmexclusively. - Component Testing: Use Vitest and React Testing Library. All asynchronous state mutations must be wrapped in
act(...)to prevent race conditions. - Icons: Use
lucide-reactexclusively. - Tailwind v4: No
tailwind.config.ts. All configuration sits inapp/globals.cssusing@themeblocks.
2. Inward Dependencies & Routing (The Ports & Adapters Mapping)
The codebase follows a strict inward-facing dependency graph.
- Entrypoints (
app/): React Server Components (RSC) act as the primary inbound adapters, parsing URL parameters and streaming Server Actions. - UI & Features (
components/): Client and Server components. Cannot house business logic APIs. - Service/Domain (
hooks/&lib/core/): Local state execution, custom hooks, and pure utilities. - Providers (
lib/providers/): Secondary adapters. Schemas (Zod) and API generated clients that reach out to our backend. Cannot import fromcomponents/orapp/.
3. The Backend API Proxy
To ensure environment-agnostic Docker deployments without build-time variable inlining:
- All Orval-generated Next.js clients fetch relative URLs (
/api/...). - A Next.js Route Handler interceptor (
app/api/[...proxy]/route.ts) acts as a server-side reverse proxy. - It proxies incoming requests to the URL specified by
CORE_API_URLat runtime.
4. Authentication (Clerk)
- Identity: We utilize
@clerk/nextjsfor all interactive flows. - Token passing: The Orval fetcher instance intercepts requests to
/api/...and injects the Clerk JWT inside theAuthorizationheader.
5. State & Data Validation
- Server Actions: All mutations use React Server Actions returning the Result pattern (
{ data: T, error: string | null }). - Zod Schemas: API contracts dictate the interface. Do not write handwritten TypeScript definitions. Always
z.inferfrom the generated Zod schemas. - Exhaustive Matching:
switchstatements must terminate with an exhaustivedefault: nevercheck.