5.4 KiB
5.4 KiB
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:
- Input parsing — Validate items, extract dimensions
- Provider detection — Check available API keys, select best provider chain
- Web search — Tavily API searches per item
- LLM synthesis — Structured JSON generation via Perplexity or OpenAI
- Validation — Runtime type checking of LLM output
- 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
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 |