Skip to content

[Feature] Persistent Chat History with MongoDB Storage and Conversation Sidebar #1

@Franpastoragusti

Description

@Franpastoragusti

Problem Statement

Currently, the AI chat application stores conversations only in memory (via InMemoryConversationRepository), which means:

  • No persistence: All chat history is lost when the user closes the tab or refreshes the page
  • No conversation management: Users cannot view, switch between, or manage past conversations
  • Limited user value: Users must re-explain context in every new session

The application has a well-designed hexagonal architecture with domain entities and repository interfaces already in place, but lacks a persistent storage adapter and UI for conversation management.

User Value

For end users:

  • Never lose conversations: All chat history is automatically saved to MongoDB and persists across sessions
  • Easy access: View and switch between past conversations via a collapsible sidebar
  • Better organization: Filter conversations by status (Active/Archived), see timestamps, and manage old conversations
  • Improved productivity: Resume previous conversations without re-explaining context

Concrete examples:

  • A user researching a topic can return the next day and continue where they left off
  • A user can switch between multiple ongoing conversations (e.g., one for coding help, another for weather queries)
  • A user can archive old conversations to declutter their workspace while keeping history intact

Architecture Overview

This feature requires implementing both backend and frontend components following the existing hexagonal architecture:

Backend (src/infrastructure/adapters/database/):

  • MongoDBClient.ts - Singleton connection manager with pooling
  • MongoDBConversationRepository.ts - Repository implementation
  • ConversationDocumentMapper.ts - Entity-to-document mapper
  • Updated DependencyContainer.ts - Async initialization + repository selection

Frontend (app/features/conversation/):

  • New conversation sidebar component (shadcn/ui Sidebar)
  • Conversation list with 50-100 items, sorted by recent activity
  • Enhanced useConversation hook with loadConversation() method
  • React Query integration for list fetching, optimistic updates, cache invalidation

API Endpoints:

  • GET /api/conversations/list?status=active&limit=100 - List conversations
  • DELETE /api/conversations/:id - Hard delete conversation
  • GET /api/health - MongoDB connection health check (Phase 2)

Technical Decisions Already Made

All architectural decisions have been documented by specialized agents:

Backend (.claude/doc/chat_history/backend.md):

  • Connection pooling: MaxPoolSize=10, 60s idle timeout
  • Document schema: Embedded messages (not referenced)
  • Indexes: { _id: 1 }, { updatedAt: -1 }, { status: 1, updatedAt: -1 }
  • Error handling: Graceful fallback to InMemory on MongoDB failure
  • Pagination: Skip + limit (100 default)

Frontend (.claude/doc/chat_history/frontend-data-architecture.md):

  • State management: Keep existing useConversationStorage (sessionStorage)
  • Caching: React Query with hierarchical query keys
  • Optimistic updates: Delete mutations with rollback on error
  • No URL params syncing (initially), no Suspense (traditional loading states)

UI/UX (.claude/doc/chat_history/sidebar-ui-design.md):

  • Component: shadcn/ui v4 Sidebar (collapsible, responsive, accessible)
  • Layout: 280px desktop, full overlay mobile, hamburger menu toggle
  • Actions: New Chat (default button), Delete (ghost + confirmation dialog)
  • States: Loading (skeleton), empty, error, active highlighting

Definition of Done

Implementation Complete:

  • MongoDBClient singleton with connection pooling and retry logic
  • MongoDBConversationRepository implementing IConversationRepository interface
  • ConversationDocumentMapper with entity restoration via Conversation.restore()
  • Updated DependencyContainer with async initialization and REPOSITORY_TYPE env variable
  • GET /api/conversations/list endpoint with status filtering and pagination
  • DELETE /api/conversations/:id endpoint for hard delete
  • Conversation sidebar component with shadcn/ui Sidebar, ScrollArea, AlertDialog
  • Conversation list with title, preview, timestamp, delete button, active state
  • Enhanced useConversation hook with loadConversation(conversationId) method
  • React Query hooks: useConversationsListQuery, useDeleteConversationMutation
  • Responsive behavior: Overlay on mobile (<768px), collapsible on desktop
  • Empty state, loading state, error state with retry button
  • Filter by status: All, Active, Archived with localStorage persistence

Edge Cases Handled:

  • MongoDB connection failure → Fallback to InMemory + warning log
  • Very long conversation titles → Truncate to 50 chars + "..." with tooltip
  • Rapid conversation switching → Cancel pending requests, load last clicked
  • Empty message content → Validation error
  • Conversation at 1,000 message limit → Domain error with user prompt

Unit Tests Added (>80% coverage):

  • Backend unit tests:
    • MongoConversationRepository.unit.test.ts (mocked MongoDB client)
    • ConversationDocumentMapper.test.ts (round-trip entity → document → entity)
    • ManageConversationUseCase.test.ts (mocked repository)
  • Frontend unit tests:
    • useConversationsListQuery.test.tsx (fetch, error, caching, stale time)
    • useDeleteConversationMutation.test.tsx (optimistic updates, rollback)
    • Sidebar.test.tsx (rendering, collapse, filters, states, accessibility)
    • ConversationListItem.test.tsx (click, delete, active state, truncation)

Integration Tests:

  • MongoConversationRepository.integration.test.ts (mongodb-memory-server)
    • Full CRUD lifecycle, pagination, filtering, concurrent operations
  • Frontend integration tests:
    • conversation-switching.test.tsx (sidebar → chat flow)
    • delete-conversation.test.tsx (delete → refetch → UI update)
    • filter-conversations.test.tsx (filter changes → query updates)

Documentation Updated:

  • .env.example with MONGODB_URL, DATABASE_NAME, REPOSITORY_TYPE variables
  • README.md with MongoDB setup instructions
  • Architecture documentation in .claude/doc/chat_history/ (already created)

Code Review Approved:

  • Hexagonal architecture compliance verified (no domain dependencies on infrastructure)
  • Repository pattern correctly implemented
  • Dependency injection via container
  • Error handling with graceful degradation

CI/CD Passes:

  • All unit tests pass (Vitest)
  • All integration tests pass (mongodb-memory-server)
  • ESLint passes
  • TypeScript compilation succeeds
  • Build succeeds (yarn build)

Manual Testing Complete:

  • Create new conversation → First message sent → Conversation appears in sidebar
  • Click conversation in sidebar → Messages load → Active state highlighted
  • Click "New Chat" → Current conversation cleared → Input focused
  • Delete conversation → Confirmation dialog → Hard delete → Refetch list
  • Filter by status → Active/Archived conversations shown correctly
  • Responsive: Mobile overlay with backdrop, desktop collapsible sidebar
  • Error handling: MongoDB down → Fallback to InMemory → Warning displayed
  • Performance: Sidebar loads <2s with 50 conversations

Manual Testing Checklist

Basic Flow (5 minutes):

  1. Open app in fresh browser tab
  2. Send first message: "What is the weather in San Francisco?"
  3. Verify conversation appears in sidebar with title "What is the weather in San Francisco?"
  4. Send second message: "And what about New York?"
  5. Refresh page → Verify conversation persists and messages reload
  6. Click "New Chat" button → Verify chat clears and input is focused
  7. Send message in new conversation → Verify second conversation appears in sidebar
  8. Click first conversation → Verify messages load correctly
  9. Click delete button on second conversation → Confirm deletion → Verify removed from list

Edge Case Testing (3 minutes):

  1. Send message with 500+ characters → Verify title truncates to 50 chars + "..."
  2. Hover over truncated title → Verify tooltip shows full title
  3. Rapidly click between 3 conversations → Verify last clicked loads without errors
  4. Try to send empty message (whitespace only) → Verify validation error

Error Handling (3 minutes):

  1. Disconnect internet → Try to load sidebar → Verify error state with retry button
  2. Click retry → Verify loading state → Reconnect internet → Verify list loads
  3. Kill MongoDB (set invalid MONGODB_URL) → Refresh app → Verify fallback to InMemory + console warning

Integration (2 minutes):

  1. Open app in two browser tabs with same conversation
  2. Send message in tab 1 → Switch to tab 2 → Refresh → Verify message appears
  3. Delete conversation in tab 1 → Switch to tab 2 → Refresh → Verify conversation gone

Responsive Behavior (2 minutes):

  1. Desktop (1920px): Verify sidebar is 280px wide, collapsible with hamburger icon
  2. Resize to tablet (768px): Verify sidebar pushes content, 320px wide
  3. Resize to mobile (375px): Verify sidebar is full overlay with backdrop
  4. Click conversation on mobile → Verify sidebar auto-closes after selection
  5. Test keyboard shortcut (Cmd/Ctrl+B) → Verify sidebar toggles

Acceptance Criteria Reference

Critical Path (Must Pass):

  • AC-8.1.1: Zero message loss - All sent messages persisted successfully
  • AC-8.2.1, AC-8.2.2: No duplicate conversations (UUID uniqueness)
  • AC-8.3.1, AC-8.3.2: Correct message ordering (server timestamps)
  • AC-6.1.1, AC-6.1.2: Load time <2s for list, <1.5s for conversation
  • AC-4.2.2, AC-4.2.3: Hard delete with confirmation dialog
  • AC-5.1.1, AC-5.1.3: MongoDB connection resilience (fallback to InMemory)

Important (Should Pass):

  • AC-6.2.1: Sidebar 60fps animations (300ms transition)
  • AC-NFR-1, AC-NFR-2, AC-NFR-3: WCAG 2.1 AA accessibility compliance
  • AC-5.1.3: Error auto-recovery 90% success rate
  • AC-7.1.1, AC-7.2.1, AC-7.3.1: Responsive design across 3 breakpoints

Complete acceptance criteria: .claude/doc/chat_history/acceptance-criteria.md (75+ criteria)

Implementation Plan

Phase 1: Backend Infrastructure (Priority: HIGH):

  1. Install mongodb driver: yarn add mongodb
  2. Install test dependencies: yarn add -D mongodb-memory-server
  3. Create MongoDBClient singleton with connection pooling
  4. Create ConversationDocumentMapper with entity restoration
  5. Implement MongoDBConversationRepository
  6. Update DependencyContainer for async initialization
  7. Add GET /api/conversations/list endpoint
  8. Add DELETE /api/conversations/:id endpoint
  9. Write unit tests (mocked MongoDB)
  10. Write integration tests (mongodb-memory-server)

Phase 2: Frontend Data Layer (Priority: HIGH):

  1. Install MSW for testing: yarn add -D msw@latest
  2. Create conversation list schema (Zod)
  3. Enhance ConversationService with listConversations() and error handling
  4. Create useConversationsListQuery hook
  5. Enhance useDeleteConversationMutation with optimistic updates
  6. Enhance useConversation hook with loadConversation() method
  7. Create useSwitchConversation business hook
  8. Write unit tests for hooks and service

Phase 3: UI Components (Priority: HIGH):

  1. Install shadcn sidebar: npx shadcn-ui@latest add sidebar
  2. Create ConversationSidebar component (basic structure)
  3. Create ConversationListItem component (title, preview, timestamp, delete)
  4. Create ConversationList component (ScrollArea + list mapping)
  5. Add empty state, loading state (skeleton), error state
  6. Add filter buttons (All, Active, Archived)
  7. Integrate sidebar in layout.tsx with SidebarProvider
  8. Add SidebarTrigger to Navbar
  9. Write component tests (React Testing Library)

Phase 4: Integration & Polish (Priority: MEDIUM):

  1. Test full user flows (create, load, delete, filter)
  2. Test responsive behavior on 3 viewports
  3. Test error scenarios (MongoDB down, network errors)
  4. Verify accessibility (keyboard navigation, screen reader)
  5. Performance testing (50+ conversations)
  6. Write integration tests
  7. Update documentation (README, .env.example)

Phase 5: Monitoring & Optimization (Priority: LOW - Future):

  • Add /api/health endpoint for MongoDB connection health
  • Implement query performance monitoring
  • Add structured logging (pino/winston)
  • Cursor-based pagination for >100 conversations
  • Real-time updates via WebSocket (multi-device sync)

Dependencies

Environment Variables Required:

MONGODB_URL=mongodb+srv://user:pass@cluster.mongodb.net/
DATABASE_NAME=ai_chat_app
REPOSITORY_TYPE=mongodb  # or 'inmemory' for testing

New Packages:

  • mongodb - MongoDB Node.js driver
  • mongodb-memory-server (dev) - In-memory MongoDB for integration tests
  • msw (dev) - Mock Service Worker for frontend testing

Existing Dependencies (Already Installed):

  • @tanstack/react-query - State management
  • axios - HTTP client
  • vitest - Test runner
  • @testing-library/react - Component testing
  • shadcn/ui - UI components

Related Documentation

All planning documents are in .claude/doc/chat_history/:

  • backend.md - MongoDB repository architecture (8,000+ words)
  • frontend-data-architecture.md - React Query integration (6,000+ words)
  • sidebar-ui-design.md - UI/UX specifications (5,000+ words)
  • backend-testing-strategy.md - Testing approach (4,000+ words)
  • frontend-testing-strategy.md - Frontend testing (4,500+ words)
  • acceptance-criteria.md - 75+ acceptance criteria (11,500+ words)
  • validation-checklist.md - Manual testing guide (quick reference)
  • test-scenario-mapping.md - 135 test scenarios mapped to ACs

Session context: .claude/sessions/context_session_chat_history.md

Notes

  • Architecture compliance: This feature strictly follows hexagonal architecture principles (zero domain dependencies on infrastructure)
  • Incremental rollout: Use REPOSITORY_TYPE=inmemory for local development, REPOSITORY_TYPE=mongodb for production
  • Testing strategy: Dual approach - mocked unit tests + mongodb-memory-server integration tests
  • MongoDB setup: MongoDB Atlas already configured at mongodb+srv://connection:***@gurusup-prod.kyw62.mongodb.net
  • No migration needed: Fresh start - no data to migrate from InMemory

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions