ADHD-Closet

System Architecture

Wardrobe AI Closet technical architecture and data flow diagrams.

Table of Contents

  1. High-Level Overview
  2. Component Architecture
  3. Data Flow
  4. AI Job Processing
  5. Caching Strategy
  6. Database Schema

High-Level Overview

┌─────────────────┐
│   Web Browser   │
│  (React/Next)   │
└────────┬────────┘
         │ HTTPS
         ▼
┌─────────────────────────────────────┐
│       Next.js App Router            │
│  ┌──────────┐    ┌──────────────┐  │
│  │  Pages   │    │  API Routes  │  │
│  │ (UI/UX)  │◄───┤  (REST API)  │  │
│  └──────────┘    └───────┬──────┘  │
│                          │          │
└──────────────────────────┼──────────┘
                           │
         ┌─────────────────┼─────────────────┐
         │                 │                 │
         ▼                 ▼                 ▼
  ┌───────────┐     ┌────────────┐    ┌──────────┐
  │ PostgreSQL│     │   Redis    │    │   Disk   │
  │   (Data)  │     │  (Queue)   │    │ (Images) │
  └───────────┘     └─────┬──────┘    └──────────┘
                          │
                          ▼
                   ┌──────────────┐
                   │  BullMQ      │
                   │  AI Workers  │
                   └──────┬───────┘
                          │
                          ▼
                   ┌──────────────┐
                   │  OpenRouter  │
                   │   AI API     │
                   └──────────────┘

Technology Stack


Component Architecture

Frontend Layers

┌──────────────────────────────────────┐
│            Pages (Routes)            │
│  /               - Home              │
│  /items/[id]     - Item Detail       │
│  /items/new      - Add Item          │
│  /outfits        - Outfit History    │
│  /outfits/gen    - Outfit Generator  │
│  /analytics      - Analytics         │
│  /settings       - Settings          │
│  /closet-rail    - 3D View           │
└──────────────┬───────────────────────┘
               │
┌──────────────▼───────────────────────┐
│       Components (Reusable)          │
│  - ItemCard      - FilterPanel       │
│  - ItemGrid      - CategoryTabs      │
│  - BulkEditTable - EnhancedSearch    │
│  - AddItemBtn    - NotificationPrompt│
└──────────────┬───────────────────────┘
               │
┌──────────────▼───────────────────────┐
│          Library (Utilities)         │
│  - hooks/          - types/          │
│  - utils/          - theme           │
│  - notifications   - prisma client   │
└──────────────────────────────────────┘

Backend Layers

┌──────────────────────────────────────┐
│          API Routes (REST)           │
│  /api/items              (CRUD)      │
│  /api/items/[id]         (CRUD)      │
│  /api/items/[id]/images  (Upload)    │
│  /api/outfits            (CRUD)      │
│  /api/outfits/generate   (AI)        │
│  /api/images/[id]        (Stream)    │
│  /api/ai/jobs            (Status)    │
│  /api/export             (Backup)    │
│  /api/import             (Restore)   │
│  /api/reminders          (Laundry)   │
│  /api/tags               (CRUD)      │
└──────────────┬───────────────────────┘
               │
┌──────────────▼───────────────────────┐
│         Business Logic Layer         │
│  - Validation (Zod schemas)          │
│  - Database operations (Prisma)      │
│  - File operations (fs/sharp)        │
│  - Queue management (BullMQ)         │
└──────────────┬───────────────────────┘
               │
┌──────────────▼───────────────────────┐
│           Data Layer                 │
│  - Prisma Client (ORM)               │
│  - Redis Client (ioredis)            │
│  - File System (Node fs)             │
└──────────────────────────────────────┘

Data Flow

1. Add Item Flow

User takes photo
     │
     ▼
[Upload to /api/items/[id]/images]
     │
     ├─► Save to disk (DATA_DIR/images)
     ├─► Create ImageAsset record (Prisma)
     └─► Enqueue AI jobs:
         ├─► generate_catalog_image (if original_main)
         ├─► generate_catalog_image (if original_back)
         ├─► infer_item (category, colors, tags)
         └─► extract_label (if label_brand/label_care)
              │
              ▼
         [BullMQ Worker picks up jobs]
              │
              ├─► Call OpenRouter API
              ├─► Process response
              ├─► Save results to DB
              └─► Mark job as succeeded/failed
                   │
                   ▼
              [UI polls for status]
                   │
                   └─► Update item display

2. Generate Outfit Flow

User sets constraints (weather, vibe, occasion)
     │
     ▼
[POST /api/outfits/generate]
     │
     ├─► Fetch available items (state = available)
     ├─► Filter by constraints
     ├─► Build prompt for AI
     │
     ▼
[Enqueue generate_outfit job]
     │
     ▼
[BullMQ Worker]
     │
     ├─► Call OpenRouter API
     ├─► Parse structured response
     ├─► Create Outfit + OutfitItem records
     └─► Return outfit ID
          │
          ▼
     [Redirect user to outfit view]

3. Export/Import Flow

Export:

User clicks "Export Wardrobe"
     │
     ▼
[POST /api/export]
     │
     ├─► Query all data (items, outfits, tags)
     ├─► Generate JSON export
     ├─► Generate CSV files
     ├─► Create ZIP archive
     │    ├─► export.json
     │    ├─► items.csv
     │    ├─► outfits.csv
     │    ├─► images/ (copy all)
     │    └─► manifest.json
     │
     └─► Stream ZIP to user

Import:

User uploads ZIP backup
     │
     ▼
[POST /api/import]
     │
     ├─► Extract ZIP to temp directory
     ├─► Validate manifest
     ├─► Parse export.json
     │
     ├─► If mode = "replace":
     │    └─► Delete all existing data
     │
     ├─► If mode = "merge":
     │    └─► Skip duplicates (by ID)
     │
     ├─► Import data:
     │    ├─► Tags
     │    ├─► Items
     │    ├─► Images (copy files)
     │    └─► Outfits
     │
     └─► Return import summary

AI Job Processing

Job Lifecycle

┌─────────────┐
│   Queued    │  Job created, waiting for worker
└──────┬──────┘
       │
       ▼
┌─────────────┐
│   Running   │  Worker picked up job, calling AI API
└──────┬──────┘
       │
       ├──► Success ──┐
       │              ▼
       │         ┌─────────────┐
       │         │  Succeeded  │  Result saved to DB
       │         └─────────────┘
       │
       ├──► Failure ──┐
       │              ▼
       │         ┌─────────────┐
       │         │   Failed    │  Error logged, retry?
       │         └──────┬──────┘
       │                │
       │         (if attempts < maxAttempts)
       │                │
       └────────────────┘
                        
       └──► Needs Review ──┐
                           ▼
                      ┌──────────────┐
                      │Needs Review  │  AI uncertain, human review needed
                      └──────────────┘

Job Types

1. generate_catalog_image

2. infer_item

3. extract_label

4. generate_outfit

Worker Configuration

// lib/ai/worker.ts
const worker = new Worker('ai-jobs', async (job) => {
  switch (job.data.type) {
    case 'generate_catalog_image':
      return await generateCatalogImage(job.data);
    case 'infer_item':
      return await inferItem(job.data);
    case 'extract_label':
      return await extractLabel(job.data);
    case 'generate_outfit':
      return await generateOutfit(job.data);
  }
}, {
  connection: redis,
  concurrency: 3,  // Process 3 jobs concurrently
  removeOnComplete: { age: 86400 },  // Keep completed jobs for 24h
  removeOnFail: { age: 604800 },     // Keep failed jobs for 7 days
});

Caching Strategy

1. Image Caching

2. Database Query Caching

Currently no caching layer. Future considerations:

3. Static Assets


Database Schema

Entity Relationship Diagram

┌─────────────┐
│    Tag      │
│─────────────│
│ id (PK)     │
│ name (UQ)   │
└──────┬──────┘
       │
       │ M:N
       │
┌──────▼──────┐       ┌──────────────┐
│   ItemTag   │       │     Item     │
│─────────────│       │──────────────│
│ itemId (FK) │◄──────┤ id (PK)      │
│ tagId (FK)  │       │ title        │
└─────────────┘       │ category     │
                      │ state        │
                      │ brand        │
                      │ colorPalette │
                      │ attributes   │
                      └──────┬───────┘
                             │
                             │ 1:N
                             │
                      ┌──────▼───────┐
                      │  ImageAsset  │
                      │──────────────│
                      │ id (PK)      │
                      │ itemId (FK)  │
                      │ kind (ENUM)  │
                      │ filePath     │
                      │ width, height│
                      └──────────────┘
                             │
                             │ 1:N
                             │
                      ┌──────▼───────┐
                      │    AIJob     │
                      │──────────────│
                      │ id (PK)      │
                      │ itemId (FK)  │
                      │ type (ENUM)  │
                      │ status       │
                      │ outputJson   │
                      │ error        │
                      └──────────────┘

┌─────────────┐       ┌──────────────┐
│   Outfit    │       │  OutfitItem  │
│─────────────│       │──────────────│
│ id (PK)     │◄──────┤ outfitId (FK)│
│ title       │       │ itemId (FK)  │
│ rating      │       │ role (ENUM)  │
│ weather     │       └──────┬───────┘
│ vibe        │              │
│ explanation │              │
└─────────────┘              │
                             │
                      ┌──────▼───────┐
                      │     Item     │
                      │──────────────│
                      │ id (PK)      │
                      └──────────────┘

Key Design Decisions

1. ImageAsset separate from Item

2. Tags as separate entities

3. Outfit references Items, not copies

4. AIJob tracks all AI operations

5. JSON fields for flexible data


Performance Considerations

Bottlenecks

  1. Image generation: 10-30 seconds per image
  2. Database queries: N+1 problem with relations
  3. Image uploads: Large files (5-50MB)
  4. Export/Import: ZIP creation with many files

Optimizations

  1. Thumbnail generation: WebP format, 512px max
  2. Prisma includes: Only fetch needed relations
  3. Queue concurrency: Process 3 AI jobs in parallel
  4. Image streaming: Stream large files, don’t buffer
  5. Lazy loading: Load images on-demand, not all at once

Security Considerations

Single-User Design

Data Protection

Input Validation


Scalability

Current Limits

Future Multi-User Considerations

Would require:


Monitoring & Observability

Current

Future


Security & Privacy

Threat Model

Requirements

Performance Budget

Images

Lists

Three.js

Jobs


References