# ComparAIson — Architecture ## System Overview ``` ┌─────────────┐ ┌──────────────┐ ┌─────────────┐ │ Browser │────▶│ Traefik │────▶│ Next.js │ │ (React SPA) │◀────│ (Reverse │◀────│ (Port 3000)│ │ │ │ Proxy) │ │ │ └─────────────┘ └──────────────┘ └──────┬──────┘ │ ┌─────────────┼─────────────┐ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │PostgreSQL│ │ Tavily │ │ OpenAI │ │ (DB) │ │ (Search) │ │ (LLM) │ └──────────┘ └──────────┘ └──────────┘ ┌──────────┐ │Perplexity│ │ (LLM) │ └──────────┘ ``` ## Component Architecture ### Frontend (Next.js App Router) ``` Route Groups: ├── (auth)/ — Unauthenticated pages (sign-in, sign-up) ├── (main)/ — Authenticated pages with shared nav layout │ ├── compare/ — Comparison creation + results viewing │ ├── explore/ — Public feed browsing │ └── profile/ — User profile + comparison history └── api/ — Server-side API routes ├── auth/ — Better Auth endpoints └── compare/ — SSE research streaming endpoint ``` **Key patterns:** - **Server Components** for data-fetching pages (profile, explore) - **Client Components** for interactive UI (compare input, charts, streaming) - **Server Actions** for mutations (save comparison, update profile) - **SSE streaming** for real-time research progress ### Backend Services #### Research Pipeline (`src/lib/llm/`) Orchestrates the multi-stage research process: 1. **Input parsing** — Validate items, extract dimensions 2. **Provider detection** — Check available API keys, select best provider chain 3. **Web search** — Tavily API searches per item 4. **LLM synthesis** — Structured JSON generation via Perplexity or OpenAI 5. **Validation** — Runtime type checking of LLM output 6. **Persistence** — Store results in PostgreSQL via Drizzle ORM #### Auth System (`src/lib/auth.ts`) - Better Auth with Drizzle adapter - Email + password authentication - 7-day session expiry - Middleware-based route protection #### Database (`src/lib/db/`) - Drizzle ORM with PostgreSQL - 5 tables: users, sessions, comparisons, comparison_items, comparison_dimensions - JSONB columns for flexible research data storage - Indexed on userId, slug, status for query performance ## Data Flow ### Comparison Creation Flow ``` 1. User enters items + query on /compare 2. Client calls POST /api/compare with { items, query, dimensions } 3. Server creates comparison record (status: "researching") 4. Server opens SSE stream to client 5. Research pipeline runs: a. Parsing → emits SSE "parsing" event b. For each item: - Tavily search → emits SSE "searching" event - Process results → emits SSE "researching" event with progress % c. Synthesize all data → emits SSE "synthesizing" event d. Validate + structure → emits SSE "complete" event with full data 6. Server persists results to comparisons + comparison_items tables 7. Client renders visualization components with result data 8. User redirected to /compare/[slug] with full results ``` ### Comparison Viewing Flow ``` 1. User navigates to /compare/[slug] 2. Server component fetches comparison from DB by slug 3. If status === "researching": show streaming progress UI 4. If status === "completed": render full results with all viz components 5. If status === "failed": show error with retry option 6. Increment viewCount on each visit ``` ## Deployment Architecture ### Docker Compose ```yaml services: app: # Next.js standalone (~150-300MB RAM) db: # PostgreSQL 16 Alpine (~200-400MB RAM) ``` Total estimated RAM: **400-800MB** — fits comfortably on an 8GB Raspberry Pi. ### Traefik Integration The app is exposed via Traefik reverse proxy with: - HTTPS termination - Domain routing (e.g., `comparaison.tophermayor.com`) - Automatic SSL certificate management ## Error Handling | Layer | Strategy | |---|---| | LLM API failures | 3x retry with exponential backoff | | Provider unavailable | Automatic fallback to next provider in chain | | Invalid LLM output | Runtime validation + retry with new prompt | | Database errors | Transaction rollback, error logged, user sees "failed" status | | SSE connection lost | Client auto-reconnects, polls comparison status from DB | | Auth failures | Redirect to sign-in with callback URL |