16 KiB
Architecture
Analysis Date: 2026-04-18
Pattern Overview
Overall: Server-first Next.js App Router monolith with localized page routes, thin API handlers, Prisma-backed data access, and small client-side interaction islands.
Key Characteristics:
- Use
src/app/layout.tsxandsrc/app/[locale]/layout.tsxas the primary composition roots, with most route files implemented as async server components. - Keep browser-only interaction in explicit client components such as
src/app/[locale]/projects/ProjectsPageClient.tsx,src/app/[locale]/projects/ProjectsResultsClient.tsx,src/components/search/AISearchBar.tsx, andsrc/components/signals/SignalFeedClient.tsx. - Centralize most project and home-page reads in
src/hooks/useProjects.tsandsrc/hooks/useHome.ts, even though these modules are named like React hooks. - Validate external input with Zod schemas from
src/lib/validations.tsbefore querying or mutating the database. - Use Prisma as the only persistence client through
src/lib/prisma.ts, with schema ownership inprisma/schema.prisma.
Layers
Routing and Layout Layer:
- Purpose: Resolve locale-aware routes, shared layout chrome, SEO metadata, and global CSS.
- Location:
src/app/layout.tsx,src/app/[locale]/layout.tsx,src/app/[locale]/page.tsx,src/app/[locale]/projects/page.tsx,src/app/[locale]/projects/[id]/page.tsx,src/app/[locale]/signals/page.tsx,src/app/[locale]/about/page.tsx,src/app/sitemap.ts,src/app/robots.ts,src/middleware.ts,src/i18n/request.ts - Contains: App Router entry points,
generateMetadata,generateStaticParams,revalidateexports, locale middleware, and SEO documents. - Depends on:
next-intl, server query modules insrc/hooks, UI components insrc/components, and shared helpers insrc/lib. - Used by: The Next.js runtime.
UI Composition Layer:
- Purpose: Render reusable view fragments for pages and route-specific UI sections.
- Location:
src/components/home/*,src/components/layout/*,src/components/locale/*,src/components/project/*,src/components/search/*,src/components/signals/* - Contains: Mostly presentational components, plus a few server components that call translations or helpers directly, such as
src/components/project/ProjectList.tsxandsrc/components/project/ProjectSidebar.tsx. - Depends on: Translation APIs, route props, and typed data returned by
src/hooks/useProjects.tsorsrc/hooks/useHome.ts. - Used by: Route entry points under
src/app/[locale].
Client Interaction Layer:
- Purpose: Own browser-only state, URL mutation, fetch loops, local storage, and interactive filters/search.
- Location:
src/app/VercelMetrics.tsx,src/app/[locale]/projects/ProjectsPageClient.tsx,src/app/[locale]/projects/ProjectsResultsClient.tsx,src/components/layout/AnnouncementBar.tsx,src/components/locale/LocaleSwitcher.tsx,src/components/project/TagFilterPanel.tsx,src/components/search/AISearchBar.tsx,src/components/search/HomeSearchBar.tsx,src/components/search/AISearchResults.tsx,src/components/signals/SignalFeedClient.tsx - Contains:
useState/useEffect-driven UI state,window.history.replaceState,window.location.href,localStorage, and client-sidefetchcalls to/api/*. - Depends on: Page props, Next navigation APIs, and JSON APIs implemented under
src/app/api. - Used by: Server-rendered pages and layouts.
Query and Read Model Layer:
- Purpose: Encapsulate database reads, filtering, retry logic, shape transformations, and cached read models for projects, tags, and home-page aggregates.
- Location:
src/hooks/useProjects.ts,src/hooks/useHome.ts - Contains: Prisma queries, pagination logic, tag-category grouping, home-page aggregate builders, and result-type definitions such as
ProjectWithFlatTags,FilterTagCategoryGroup, andHomePageData. - Depends on:
src/lib/prisma.ts,src/lib/cache.ts,src/lib/tag-taxonomy.ts, and Prisma types from@prisma/client. - Used by: Page routes such as
src/app/[locale]/page.tsxand list APIs such assrc/app/api/projects/route.ts.
API and Mutation Layer:
- Purpose: Expose JSON endpoints for list/detail reads, AI search proxying, signals feed reads, authenticated tag maintenance, project-tag resets, and signal ingestion.
- Location:
src/app/api/projects/route.ts,src/app/api/projects/[slug]/route.ts,src/app/api/search/ai/route.ts,src/app/api/signals/route.ts,src/app/api/tags/route.ts,src/app/api/tags/maintenance/route.ts,src/app/api/tags/maintenance/service.ts,src/app/api/tags/reset-projects/route.ts,src/app/api/webhook/signals/route.ts - Contains:
GETandPOSTroute handlers, request parsing, validation, response shaping, cache revalidation, and transaction-scoped maintenance logic. - Depends on:
src/lib/validations.ts,src/lib/auth.ts,src/lib/prisma.ts,src/lib/signal-hotness.ts, and the query layer insrc/hooks. - Used by: Client components, external webhook callers, and automation clients.
Domain Utility Layer:
- Purpose: Hold cross-route domain logic that is not tied to a single route.
- Location:
src/lib/auth.ts,src/lib/cache.ts,src/lib/prisma.ts,src/lib/signal-hotness.ts,src/lib/slug.ts,src/lib/tag-taxonomy.ts,src/lib/validations.ts,src/lib/github/badges.ts - Contains: API-key verification, cache fallback helpers, Prisma singleton setup, signal hotness scoring, slug generation, tag taxonomy inference, schema validation, and GitHub link parsing.
- Depends on: Standard library APIs, Prisma, and Zod.
- Used by: Both pages and API handlers.
Persistence Layer:
- Purpose: Define and migrate the database model and seed baseline data.
- Location:
prisma/schema.prisma,prisma/migrations/*,prisma/seed.ts - Contains: PostgreSQL schema for
Project,Tag,ProjectTag,ExternalLink, andSignal, plus migrations and seed data. - Depends on: Prisma CLI and environment-provided
DATABASE_URL. - Used by:
src/lib/prisma.tsat runtime and Prisma tooling during migrations/seed.
Data Flow
Localized Page Render:
src/middleware.tsforces a locale-prefixed pathname and excludes/api,/_next, and static assets from locale routing.src/app/[locale]/layout.tsxvalidates the locale, callssetRequestLocale(locale), loads messages throughnext-intl, and wraps the tree withNextIntlClientProvider.- Route pages such as
src/app/[locale]/page.tsx,src/app/[locale]/projects/page.tsx, andsrc/app/[locale]/projects/[id]/page.tsxfetch data fromsrc/hooks/useProjects.tsorsrc/hooks/useHome.ts. - Reusable server and client components render the data, with ISR enabled through
export const revalidate = 300on the main content routes.
Traditional Projects Browse Flow:
src/app/[locale]/projects/page.tsxreadssearchParams, normalizes filters, and callsgetProjects,getFixedProjectTypeFilters, andgetTagCategoryGroupsfromsrc/hooks/useProjects.ts.src/app/[locale]/projects/ProjectsPageClient.tsxandsrc/components/project/TagFilterPanel.tsxmanage filter-panel visibility and query-string updates in the browser.src/app/[locale]/projects/ProjectsResultsClient.tsxfetches updated pages fromsrc/app/api/projects/route.tsfor client-side pagination and sorting.src/app/api/projects/route.tsvalidates query parameters with Zod, then delegates togetProjects.
AI Search Flow:
src/components/search/AISearchBar.tsxtoggles between traditional and AI mode and forwards the query back to the page-level handler.src/app/[locale]/projects/ProjectsResultsClient.tsxrequestsPOST /api/search/ai.src/app/api/search/ai/route.tsvalidates the request withProjectQuerySchema, forwards a GET request to the external N8N webhook URL fromN8N_AI_SEARCH_WEBHOOK, then hydrates returned IDs by callinggetProjectsByIdsfromsrc/hooks/useProjects.ts.- The route sorts and filters the hydrated projects again before returning JSON to the client.
Signals Feed Flow:
src/app/[locale]/signals/page.tsxrenders shell content and mountssrc/components/signals/SignalFeedClient.tsx.SignalFeedClientholds search text, debounce state, source filter, sort mode, and pagination cursor in component state.- The client calls
GET /api/signalsonsrc/app/api/signals/route.ts. src/app/api/signals/route.tsvalidates the query, queries Prisma, computes or reads hotness metadata viasrc/lib/signal-hotness.ts, and returns cursor-based pagination.
Signal Ingestion Flow:
- External automation posts to
src/app/api/webhook/signals/route.ts. - The handler validates the top-level payload and each item with
SignalWebhookPayloadSchemaandSignalIngestionInputSchemafromsrc/lib/validations.ts. src/lib/auth.tsverifiesapiKeyusingcrypto.timingSafeEqual.- The handler upserts signals in Prisma, computing fallback hotness when needed and downgrading if
hotScore/isHotcolumns are absent.
Tag Maintenance Flow:
- Clients call
POST /api/tags/maintenanceorPOST /api/tags/reset-projects. - The route validates input, authenticates with
isValidApiKey, and runs Prisma mutations. src/app/api/tags/maintenance/route.tsdelegates transactional merge/update logic tosrc/app/api/tags/maintenance/service.ts.- Successful mutations call
revalidatePathfor affected localized pages.
State Management
Server State:
- Persistent application state lives in PostgreSQL, modeled in
prisma/schema.prismaand accessed only through Prisma insrc/lib/prisma.ts. - Page-level read models are built in
src/hooks/useProjects.tsandsrc/hooks/useHome.ts.
Cached Server State:
unstable_cacheis used insrc/hooks/useProjects.ts,src/hooks/useHome.ts, andsrc/app/api/tags/route.ts.src/lib/cache.tsprovidesrunWithCacheFallbackso cached fetchers can fall back to direct database reads when Incremental Cache is unavailable.
Client UI State:
- Local component state, not a global store, drives interactivity.
src/app/[locale]/projects/ProjectsPageClient.tsxstores filter-panel expansion.src/app/[locale]/projects/ProjectsResultsClient.tsxstores AI/traditional pagination, loading flags, and sort/limit state.src/components/signals/SignalFeedClient.tsxstores debounced search, source filters, sort mode, cursor, and loading state.src/components/layout/AnnouncementBar.tsxstores dismissal state inlocalStorage.
URL State:
- Search, pagination, sort, AI mode, and taxonomy filters are encoded in the query string on
src/app/[locale]/projects/page.tsx. - The projects UI treats the URL as the source of truth and synchronizes local state to it through
router.pushandwindow.history.replaceState.
Translation State:
- Locale selection is path-based and enforced by
src/middleware.ts. - Message bundles load from
src/messages/en.jsonandsrc/messages/zh.jsonviasrc/i18n/request.ts.
Key Abstractions
Project Read Model:
- Purpose: Present projects with flattened tags and related links, independent of Prisma join-table shape.
- Examples:
ProjectWithFlatTagsandgetProjectsinsrc/hooks/useProjects.ts,getProjectBySluginsrc/hooks/useProjects.ts - Pattern: Read-model transformation from Prisma includes to page/API-friendly objects.
Tag Taxonomy:
- Purpose: Normalize and classify project tags into fixed categories used by filters and maintenance APIs.
- Examples:
src/lib/tag-taxonomy.ts,getTagCategoryGroupsinsrc/hooks/useProjects.ts - Pattern: Central domain vocabulary with inference helpers shared across pages and mutations.
Signal View Model:
- Purpose: Convert raw
Signalrows, localized text fields, JSON sections, and hotness metadata into feed-ready objects. - Examples:
toSignalViewandparseSectionsinsrc/app/api/signals/route.ts - Pattern: API-specific presentation mapping on top of Prisma rows.
Maintenance Service:
- Purpose: Keep complex tag merge/update rules out of the route handler.
- Examples:
executeTagMaintenanceandTagMaintenanceApiErrorinsrc/app/api/tags/maintenance/service.ts - Pattern: Route delegates orchestration to a transaction-aware service module.
Auth Guard for Internal APIs:
- Purpose: Reuse constant-time API-key validation across maintenance and webhook routes.
- Examples:
isValidApiKeyinsrc/lib/auth.ts, used bysrc/app/api/tags/maintenance/route.ts,src/app/api/tags/reset-projects/route.ts, andsrc/app/api/webhook/signals/route.ts - Pattern: Small shared guard function, not middleware-based auth.
Entry Points
Root App Shell:
- Location:
src/app/layout.tsx - Triggers: Every page render.
- Responsibilities: Global HTML/body shell, global CSS import, and conditional Vercel analytics injection through
src/app/VercelMetrics.tsx.
Localized App Shell:
- Location:
src/app/[locale]/layout.tsx - Triggers: Every localized page render under
/<locale>/*. - Responsibilities: Locale validation, translations, site navigation, announcement bar, footer, and
NextIntlClientProvider.
Home Route:
- Location:
src/app/[locale]/page.tsx - Triggers:
GET /zhandGET /en - Responsibilities: Render home hero, stats, rankings, tag insights, recent timeline, and featured projects.
Projects Route:
- Location:
src/app/[locale]/projects/page.tsx - Triggers:
GET /<locale>/projects - Responsibilities: Parse URL filters, render filter/search UI, fetch paginated project data, and mount the projects results client island.
Project Detail Route:
- Location:
src/app/[locale]/projects/[id]/page.tsx - Triggers:
GET /<locale>/projects/:slug - Responsibilities: Fetch a single project, derive related projects, render sidebar/details, and generate route metadata.
Signals Route:
- Location:
src/app/[locale]/signals/page.tsx - Triggers:
GET /<locale>/signals - Responsibilities: Render the signals feed shell and mount the client-side feed loader.
API Routes:
- Location:
src/app/api/* - Triggers: Browser fetches and external webhook clients.
- Responsibilities: JSON read endpoints, AI search proxying, signals feed pagination, authenticated tag maintenance, project tag resets, and signal ingestion.
Locale Middleware:
- Location:
src/middleware.ts - Triggers: All non-API, non-static requests.
- Responsibilities: Apply the
next-intllocale prefix policy and route matching.
Error Handling
Strategy: Validate early, return structured JSON for API errors, use notFound() for invalid page resources, and degrade some read paths to empty or fallback results instead of hard-failing the page.
Patterns:
- Use Zod validation in
src/app/api/projects/route.ts,src/app/api/search/ai/route.ts,src/app/api/signals/route.ts,src/app/api/tags/maintenance/route.ts,src/app/api/tags/reset-projects/route.ts, andsrc/app/api/webhook/signals/route.ts. - Use
notFound()insrc/app/[locale]/layout.tsx,src/app/[locale]/projects/[id]/page.tsx, andsrc/app/[locale]/[...catchAll]/page.tsxfor route-level misses. - Retry transient database errors in
src/hooks/useProjects.tsviawithDbRetry. - Degrade to empty or zero-count results in
src/hooks/useProjects.tsandsrc/hooks/useHome.tswhen some DB reads fail. - Wrap service-specific failures in
TagMaintenanceApiErrorinsrc/app/api/tags/maintenance/service.ts.
Cross-Cutting Concerns
Logging: Use console.error and console.warn directly in route handlers and server query modules, for example in src/app/api/search/ai/route.ts, src/app/api/signals/route.ts, src/app/api/webhook/signals/route.ts, src/hooks/useProjects.ts, and src/hooks/useHome.ts.
Validation: Use shared Zod schemas from src/lib/validations.ts for external request bodies and query parameters.
Authentication: Protect internal mutation/webhook endpoints with API-key checks from src/lib/auth.ts. No session or user-account auth layer is present in the inspected files.
Internationalization: Use next-intl with locale-prefixed routing through src/middleware.ts, request config in src/i18n/request.ts, and message bundles in src/messages/*.json.
Caching and Revalidation: Use ISR-style route revalidation (revalidate = 300), unstable_cache, and revalidatePath after tag mutations.
Observability: The only built-in runtime instrumentation found is Vercel analytics in src/app/VercelMetrics.tsx. No separate tracing or background job framework was detected in inspected files.
Architecture analysis: 2026-04-18