19 KiB
Architecture
Analysis Date: 2026-04-20
Pattern Overview
Overall: Localized Next.js monolith with App Router pages, colocated JSON APIs, Prisma-backed read models, and an external automation perimeter for n8n and discovery-task execution.
Key Characteristics:
- User-facing pages and internal JSON APIs ship from the same Next.js app under
src/app/. - Server components and API handlers read through shared query/read-model modules in
src/hooks/useProjects.tsandsrc/hooks/useHome.ts. - External automation is first-class context, but not fully implemented in-repo:
docs/integrations/n8n/*and.planning/codebase/N8N-*.mddocument workflows that call a subset of repository APIs and also use external discovery services or direct database writes.
Layers
Route & Shell Layer:
- Purpose: Own URL structure, metadata, page composition, SEO documents, and locale-aware layout shells.
- 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 - Contains: App Router pages,
generateMetadata,generateStaticParams, route-levelrevalidate, and top-level page composition. - Depends on:
next-intl,src/hooks/*, reusable components, and Next.js runtime APIs. - Used by: Browser requests for
/<locale>/*,sitemap.xml, androbots.txt.
Client Interaction Layer:
- Purpose: Own browser-only state, URL syncing, incremental fetching, and interactive search/filter/feed behavior.
- Location:
src/app/[locale]/projects/ProjectsPageClient.tsx,src/app/[locale]/projects/ProjectsResultsClient.tsx,src/components/search/AISearchBar.tsx,src/components/search/HomeSearchBar.tsx,src/components/search/AISearchResults.tsx,src/components/signals/SignalFeedClient.tsx,src/components/layout/AnnouncementBar.tsx,src/components/locale/LocaleSwitcher.tsx,src/app/VercelMetrics.tsx - Contains:
"use client"components, fetch calls to/api/*, local state, debounced input, history updates, and analytics instrumentation. - Depends on: Browser APIs, Next navigation hooks, and JSON returned by
src/app/api/*. - Used by: Localized page routes that mount client islands after server rendering.
Query / Read-Model Layer:
- Purpose: Encapsulate Prisma reads, filter normalization, retry logic, caching, and response shape flattening for pages and APIs.
- Location:
src/hooks/useProjects.ts,src/hooks/useHome.ts - Contains:
getProjects,getProjectBySlug,getProjectsByIds,getTagCategoryGroups,getFixedProjectTypeFilters,getTopTags, andgetHomePageData. - Depends on:
src/lib/prisma.ts,src/lib/cache.ts,src/lib/tag-taxonomy.ts, Prisma types, andnext/cache. - Used by: Server pages such as
src/app/[locale]/page.tsxand APIs such assrc/app/api/projects/route.ts.
API Integration & Mutation Layer:
- Purpose: Expose JSON endpoints for browser fetches and authenticated automation/webhook writes.
- 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: Zod request validation, Prisma queries and transactions, n8n proxying, revalidation, and webhook ingestion.
- Depends on:
src/hooks/*,src/lib/*, Prisma, and external env/config for webhook integration. - Used by: Client components in this repo, n8n workflows, and other internal automation clients.
Shared Domain & Infrastructure Layer:
- Purpose: Centralize cross-cutting rules and infrastructure helpers used by both pages and route handlers.
- Location:
src/lib/auth.ts,src/lib/cache.ts,src/lib/prisma.ts,src/lib/prisma-url.ts,src/lib/signal-hotness.ts,src/lib/slug.ts,src/lib/tag-taxonomy.ts,src/lib/validations.ts,src/lib/github/badges.ts,src/i18n/request.ts - Contains: API-key validation, Prisma bootstrap, cache fallback helpers, hotness scoring, slug generation, taxonomy metadata, and Zod schemas.
- Depends on: Node APIs, Prisma, Zod, and Next.js server utilities.
- Used by: Both
src/hooks/*andsrc/app/api/*, plus selected components.
Persistence Layer:
- Purpose: Define the application data model, migrations, and seed path.
- Location:
prisma/schema.prisma,prisma/migrations/*,prisma/seed.ts - Contains: PostgreSQL schema for
projects,external_links,tags,project_tags, andsignals, plus migration history and seed logic. - Depends on: Prisma tooling and
DATABASE_URL. - Used by:
src/lib/prisma.tsat runtime and Prisma CLI during migration/seed flows.
External Automation Context Layer:
- Purpose: Document and constrain the parts of the system that live outside this repository.
- Location:
docs/integrations/n8n/README.md,docs/integrations/n8n/registry.json,docs/integrations/n8n/DATAFLOW.md,docs/integrations/n8n/workflows/*.md,.planning/codebase/N8N-CONTEXT.md,.planning/codebase/N8N-DATAFLOW.md,scripts/generate-n8n-context.mjs - Contains: Workflow inventory, request/response contracts, source-to-DB/API/UI flows, and generated mirrors for GSD.
- Depends on: External n8n workflows, registry metadata, and repository scanning.
- Used by: Humans and GSD agents to understand how external pipelines affect the in-repo app.
Data Flow
Localized Page Render:
src/middleware.tsapplies locale-prefixed routing for non-API, non-static requests.src/app/[locale]/layout.tsxvalidates the locale, runssetRequestLocale(locale), loads messages fromsrc/i18n/request.ts, and wraps the subtree inNextIntlClientProvider.- Route pages such as
src/app/[locale]/page.tsx,src/app/[locale]/projects/page.tsx,src/app/[locale]/projects/[id]/page.tsx, andsrc/app/[locale]/signals/page.tsxfetch server data or mount client fetchers. - Reusable components under
src/components/*render the localized UI.
Traditional Projects Browse Flow:
src/app/[locale]/projects/page.tsxnormalizes search params forsearch,tags,domains,productForms,projectType,sort,page, andlimit.- The page calls
getProjects,getFixedProjectTypeFilters, andgetTagCategoryGroupsfromsrc/hooks/useProjects.ts. src/app/[locale]/projects/ProjectsPageClient.tsxcontrols search-mode switching and filter-panel expansion.src/app/[locale]/projects/ProjectsResultsClient.tsxfetches updated pages fromsrc/app/api/projects/route.tsand keeps the URL query string in sync with browser state.
AI Search Flow:
src/components/search/AISearchBar.tsxcaptures the search term and mode.src/app/[locale]/projects/ProjectsResultsClient.tsxposts the normalized request tosrc/app/api/search/ai/route.ts.src/app/api/search/ai/route.tsvalidates the body withProjectQuerySchema, forwards a GET request to the external n8n webhook fromN8N_AI_SEARCH_WEBHOOK, and receives ranked{id, similarity}candidates.- The route hydrates those IDs from PostgreSQL through
getProjectsByIdsinsrc/hooks/useProjects.ts, reapplies local sorting/filtering rules, and returns the final JSON payload.
Project Detail Flow:
src/app/[locale]/projects/[id]/page.tsxresolves the route param as a slug and cachesgetProjectBySlugwithreactcache().- The page fetches the selected project and a small recent-project set through
getProjects({ limit: 3 }). src/components/project/ProjectDetail.tsx,src/components/project/ProjectSidebar.tsx, andsrc/components/project/RelatedProjects.tsxrender the long-form page.generateMetadatain the same file derives title/description from the fetched project.
Signals Read Flow:
src/app/[locale]/signals/page.tsxrenders the shell and mountssrc/components/signals/SignalFeedClient.tsx.SignalFeedClientmanages debounced text search, source filters, sort mode, cursor pagination, and incremental loading in browser state.- The client calls
GET /api/signalsonsrc/app/api/signals/route.ts. src/app/api/signals/route.tsvalidates the request withSignalQuerySchema, queries Prisma, converts JSON sections/tags into view models, and usessrc/lib/signal-hotness.tswhenhotScore/isHotcolumns are missing or unavailable.
Signals Ingestion Flow:
- External automation posts batches to
src/app/api/webhook/signals/route.ts. - The route validates the top-level payload with
SignalWebhookPayloadSchemaand each item withSignalIngestionInputSchemafromsrc/lib/validations.ts. src/lib/auth.tschecks the shared API key with constant-time comparison.- The handler computes fallback hotness when needed and upserts each signal into Prisma.
Tag Governance Flow:
- Automation or internal tools call
POST /api/tags/maintenanceorPOST /api/tags/reset-projects. src/app/api/tags/maintenance/route.tsdelegates merge/update logic tosrc/app/api/tags/maintenance/service.tsinside a transaction.src/app/api/tags/reset-projects/route.tsvalidates requested tag slugs, resolves project/tag IDs, performs transactional tag replacement, and tracks per-project results.- Both mutation routes call
revalidatePathfor affected localized pages after successful writes.
Home Aggregate Flow:
src/app/[locale]/page.tsxcallsgetProjects({ limit: 6 })andgetHomePageData()in parallel.src/hooks/useHome.tsbuilds counts, rankings, top tags, and recent timeline data from Prisma withunstable_cache.- Home sections under
src/components/home/*render those aggregates.
External Discovery / Ingestion Boundary:
- Workflow specifications in
docs/integrations/n8n/workflows/01-topic-discovery.md,docs/integrations/n8n/workflows/02-github-trending-discovery.md, anddocs/integrations/n8n/workflows/03-project-ingestion-multi-source.mddescribe the discovery queue and task lifecycle. - The documented discovery endpoints include
POST /api/discovery/check-duplicates,POST /api/discovery/tasks,GET /api/discovery/tasks,PATCH /api/discovery/tasks/:id, andPOST /api/discovery/tasks/:id/complete. - No
/api/discovery/*implementation exists undersrc/app/api/; that queue/task service is external to this repository. - This repository consumes the results of that pipeline through the shared PostgreSQL schema and its read APIs, not by executing the queue itself.
Direct DB Maintenance Boundary:
docs/integrations/n8n/workflows/04-github-star-refresh.mdanddocs/integrations/n8n/workflows/05-project-description-vectorization.mddescribe workflows that updateprojects.githubStars,projects.githubStarsUpdatedAt,projects.embedding, andprojects.embeddingUpdatedAt.- Those workflows are documented as direct database maintenance jobs, not as repository API routes.
src/app/api/search/ai/route.tsand the home/projects pages consume the resulting columns after the external workflows finish.
State Management:
- Persistent domain state lives in PostgreSQL as modeled in
prisma/schema.prisma. - Cached server read state lives in
unstable_cachewrappers insrc/hooks/useProjects.ts,src/hooks/useHome.ts, andsrc/app/api/tags/route.ts, with fallback behavior fromsrc/lib/cache.ts. - Client UI state lives in route-local/browser components such as
src/app/[locale]/projects/ProjectsResultsClient.tsx,src/app/[locale]/projects/ProjectsPageClient.tsx,src/components/search/AISearchBar.tsx, andsrc/components/signals/SignalFeedClient.tsx. - The projects browse experience treats the query string as the canonical state for filters, sort, pagination, and AI mode.
Key Abstractions
Project Read Model:
- Purpose: Flatten Prisma join-table data into project objects that pages and APIs can render directly.
- Examples:
ProjectWithFlatTags,getProjects,getProjectBySlug, andgetProjectsByIdsinsrc/hooks/useProjects.ts - Pattern: Prisma
includequeries are transformed into a stable read model withtagsflattened fromproject_tags.
Home Aggregate Read Model:
- Purpose: Produce a homepage-specific summary instead of exposing raw Prisma rows to UI sections.
- Examples:
HomePageDataandgetHomePageDatainsrc/hooks/useHome.ts - Pattern: Aggregate counters and ranked slices are computed once, cached, and returned in a UI-ready object graph.
Tag Taxonomy:
- Purpose: Encode canonical tag categories, project-type presets, and category ordering rules in one place.
- Examples:
src/lib/tag-taxonomy.ts,getTagCategoryGroupsinsrc/hooks/useProjects.ts,isFixedProjectTypeSluginsrc/lib/tag-taxonomy.ts - Pattern: Shared domain vocabulary drives both browse filters and maintenance APIs.
Signal View Model:
- Purpose: Convert stored signal rows plus JSON payloads into localized feed cards.
- Examples:
parseSectionsandtoSignalViewinsrc/app/api/signals/route.ts - Pattern: API-specific presentation mapping handles fallback localization, section parsing, and hotness derivation.
Internal Webhook/Auth Guard:
- Purpose: Reuse constant-time API-key verification across internal write endpoints.
- 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 helper instead of a global auth middleware.
Cache Fallback Wrapper:
- Purpose: Allow cached read paths to degrade to direct fetches when Incremental Cache is unavailable.
- Examples:
runWithCacheFallbackinsrc/lib/cache.ts - Pattern: Wrap
unstable_cachefetchers so the app still serves data in environments without Incremental Cache support.
n8n Contract Registry:
- Purpose: Keep external workflow contracts reviewable inside the repo without pretending their runtime lives here.
- Examples:
docs/integrations/n8n/registry.json,docs/integrations/n8n/DATAFLOW.md,.planning/codebase/N8N-CONTEXT.md - Pattern: External workflow metadata is committed and mirrored so architecture work can describe the full system boundary accurately.
Entry Points
Root HTML Shell:
- Location:
src/app/layout.tsx - Triggers: Every page render.
- Responsibilities: Global HTML/body shell, CSS import, and conditional analytics injection through
src/app/VercelMetrics.tsx.
Locale Middleware:
- Location:
src/middleware.ts - Triggers: All non-API, non-static requests.
- Responsibilities: Apply locale prefix rules and keep
/apiand asset paths out of locale routing.
Localized App Shell:
- Location:
src/app/[locale]/layout.tsx - Triggers: Every localized page render.
- Responsibilities: Locale validation, translations, header/footer shell, announcement bar, and provider setup.
Home Route:
- Location:
src/app/[locale]/page.tsx - Triggers:
GET /zhandGET /en - Responsibilities: Render the home hero plus cached rankings and featured projects.
Projects Route:
- Location:
src/app/[locale]/projects/page.tsx - Triggers:
GET /<locale>/projects - Responsibilities: Parse query-string filters, fetch initial browse data, and mount the projects client islands.
Project Detail Route:
- Location:
src/app/[locale]/projects/[id]/page.tsx - Triggers:
GET /<locale>/projects/:slug - Responsibilities: Fetch one project, render detail/sidebar sections, derive related-project cards, and emit metadata.
Signals Route:
- Location:
src/app/[locale]/signals/page.tsx - Triggers:
GET /<locale>/signals - Responsibilities: Render the signals page shell and mount the client-side feed loader.
JSON API Surface:
- Location:
src/app/api/* - Triggers: Browser fetches and automation/webhook requests.
- Responsibilities: Read endpoints for projects/tags/signals, AI search proxying, authenticated tag maintenance, project-tag reset, and signal ingestion.
n8n Context Generator:
- Location:
scripts/generate-n8n-context.mjs - Triggers:
pnpm n8n:context - Responsibilities: Read
docs/integrations/n8n/registry.json, scansrc/for n8n/webhook touchpoints, and regeneratedocs/integrations/n8n/CONTEXT.generated.mdplus.planning/codebase/N8N-CONTEXT.md.
Error Handling
Strategy: Validate inputs early, return structured JSON errors from route handlers, use notFound() for invalid route resources, and degrade selected read paths to fallback data rather than failing the whole page render.
Patterns:
- Use shared Zod schemas from
src/lib/validations.tsinsrc/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.tsx. - Retry transient project/tag DB reads in
src/hooks/useProjects.tsthroughwithDbRetry. - Degrade cached or aggregate reads in
src/hooks/useProjects.ts,src/hooks/useHome.ts, andsrc/lib/cache.tswhen the cache/runtime is unavailable. - Convert route-specific domain failures into
TagMaintenanceApiErrorinsrc/app/api/tags/maintenance/service.ts. - Detect schema drift for signal hotness columns through
src/lib/signal-hotness.tsand downgrade to fallback sorting/calculation.
Cross-Cutting Concerns
Logging: Use console.error and console.warn directly in server code such as 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: Centralize public and internal payload/query contracts in src/lib/validations.ts.
Authentication: Protect mutation/webhook endpoints with shared API-key validation from src/lib/auth.ts. No user/session auth layer is present in the inspected app architecture.
Internationalization: Keep locale routing and messages in src/middleware.ts, src/i18n/request.ts, src/messages/en.json, and src/messages/zh.json.
Caching & Revalidation: Use unstable_cache, route-level revalidate = 300, and revalidatePath after tag mutations.
SEO & Discovery: Build sitemap.xml from getProjects in src/app/sitemap.ts and block /api/* from indexing in src/app/robots.ts.
Observability: Restrict Vercel Analytics and Speed Insights to locale-prefixed user-facing routes in src/app/VercelMetrics.tsx.
External System Boundary: Treat n8n workflows and discovery-task services as external systems unless a concrete route exists under src/app/api/. The repo documents those systems in docs/integrations/n8n/* and .planning/codebase/N8N-*.md, but does not execute the queue/task infrastructure itself.
Architecture analysis: 2026-04-20