High-Level Architecture
ThinkEx follows a client-server architecture with real-time synchronization:- Frontend: Next.js 16 with React 19, server and client components
- Backend: Next.js API routes with PostgreSQL database
- State Management: Event sourcing with optimistic updates
- Real-time: PostgreSQL triggers with broadcast for multi-user collaboration
- Authentication: Better Auth for secure user sessions
Event Sourcing Architecture
ThinkEx uses event sourcing as its core state management pattern. Instead of storing the current state directly, all changes are stored as immutable events that can be replayed to derive the current state.Why Event Sourcing?
Benefits of Event Sourcing
Benefits of Event Sourcing
- Complete Audit Trail: Every change is recorded with timestamp, user, and payload
- Time Travel: Replay events to see workspace state at any point in time
- Conflict Resolution: Events are ordered and versioned to handle concurrent edits
- Undo/Redo: Built-in support by replaying or removing events
- Real-time Sync: Events are broadcast to all collaborators immediately
Event Flow
Event Types
All workspace changes are represented as events insrc/lib/workspace/events.ts:
Workspace Events
WORKSPACE_CREATEDGLOBAL_TITLE_SETWORKSPACE_SNAPSHOT
Item Events
ITEM_CREATEDITEM_UPDATEDITEM_DELETEDBULK_ITEMS_CREATEDBULK_ITEMS_UPDATED
Folder Events
ITEM_MOVED_TO_FOLDERITEMS_MOVED_TO_FOLDERFOLDER_CREATED_WITH_ITEMS
Layout Events
BULK_ITEMS_UPDATED(layout changes)
src/lib/workspace/events.ts:9 for full event type definitions.
Event Reducer
The event reducer (src/lib/workspace/event-reducer.ts:9) is a pure function that applies events to state:
The reducer is deterministic - replaying the same events always produces the same state. This property is critical for multi-user collaboration.
Snapshot Optimization
To avoid replaying thousands of events on every load, ThinkEx creates periodic snapshots of workspace state:- Snapshots are stored in the
workspace_snapshotstable - Each snapshot includes a version number and full state
- When loading a workspace, start from the latest snapshot and replay only newer events
- Snapshots are created automatically every N events (configurable)
src/lib/workspace/snapshot-manager.ts for snapshot logic.
Workspace State Management
State Structure
Workspace state is defined insrc/lib/workspace-state/types.ts:196:
Item Types
ThinkEx supports multiple card types, all stored asItem objects:
Supported Item Types
Supported Item Types
- note: Rich-text notes using BlockNote editor
- pdf: PDF documents with annotations and OCR
- flashcard: Study flashcards with front/back content
- youtube: Embedded YouTube videos with transcripts
- image: Image cards with captions
- audio: Audio files with transcriptions and speaker diarization
- quiz: Multiple-choice and true/false quizzes
- folder: Container items for organizing cards
id: Unique identifiertype: Card typename: User-editable titledata: Type-specific data (NoteData, PdfData, etc.)layout: Position and size on canvascolor: Background colorfolderId: Parent folder (optional)lastModified: Timestamp for conflict detection
src/lib/workspace-state/types.ts:177 for full Item interface.
State Synchronization
ThinkEx uses a hybrid approach combining optimistic updates with event sourcing:- Optimistic Updates: UI updates immediately before server confirmation
- Event Broadcasting: Server broadcasts events to all connected clients
- State Reconciliation: Clients merge remote events with local state
- Conflict Detection: Version numbers detect concurrent modifications
Real-Time Collaboration
ThinkEx supports real-time multi-user collaboration where multiple users can edit the same workspace simultaneously.Collaboration Model
Workspaces have three permission levels:- Owner: Full control (delete workspace, manage collaborators)
- Editor: Can add/edit/delete cards and invite others
- Viewer: Read-only access
src/lib/db/schema.ts:188 (workspace_collaborators table).
Event Broadcasting
When a user makes a change:- Event is inserted into
workspace_eventstable - PostgreSQL trigger fires (
workspace_events_realtime_broadcast) - Event is broadcast to channel
workspace:<id>:events - All subscribed clients receive the event via WebSocket
- Clients apply event to their local state using the reducer
drizzle/0001_add_realtime_collaboration.sql:37 for trigger definition.
Conflict Resolution
ThinkEx handles conflicts using version vectors:- Each event has a
versionnumber that increments sequentially - Clients track the last version they’ve seen
- When sending events, clients include their current version
- Server rejects events with stale versions (conflict detected)
- Client must fetch latest events and retry
Presence & Cursors
ThinkEx shows live cursors for all active collaborators:- Cursor positions are broadcast via the same WebSocket channel
- Cursor data includes user name, color, and position
- Stale cursors (no update for 30s) are automatically removed
src/hooks/workspace/use-workspace-realtime.ts for real-time hooks.
Database Schema
Core Tables
workspaces
Workspace metadata: title, owner, slug, timestamps
workspace_events
Event log: all changes to workspaces
workspace_snapshots
Periodic snapshots of workspace state
workspace_collaborators
Access control: who can view/edit workspaces
chat_threads
AI conversation threads within workspaces
chat_messages
Individual messages in threads
src/lib/db/schema.ts
Row-Level Security (RLS)
All tables use PostgreSQL Row-Level Security policies:- Users can only access their own workspaces or workspaces they collaborate on
- Editors can insert events; viewers cannot
- Share links bypass RLS with token-based authentication
src/lib/db/schema.ts and migration files in drizzle/.
API Design
ThinkEx uses Next.js API routes with REST-style endpoints:Key Endpoints
Workspace Endpoints
Workspace Endpoints
GET /api/workspaces- List user’s workspacesPOST /api/workspaces- Create new workspaceGET /api/workspaces/[id]- Get workspace metadataPATCH /api/workspaces/[id]- Update workspaceDELETE /api/workspaces/[id]- Delete workspace
Event Endpoints
Event Endpoints
GET /api/workspaces/[id]/events- Fetch event logPOST /api/workspaces/[id]/events- Append new eventsPOST /api/workspaces/[id]/events/undo- Undo last event
Collaboration Endpoints
Collaboration Endpoints
GET /api/workspaces/[id]/collaborators- List collaboratorsPOST /api/workspaces/[id]/collaborators- Invite collaboratorDELETE /api/workspaces/[id]/collaborators/[id]- Remove collaborator
src/app/api/.
Design Patterns
Event Sourcing
All state changes are events that can be replayed. See Event Sourcing section above.Optimistic UI
UI updates immediately before server confirmation, then reconciles on success/failure.Repository Pattern
Database access is abstracted through service layers insrc/lib/.
Factory Pattern
Workspace templates use factories to generate initial state:src/lib/workspace/templates.ts.
Observer Pattern
Real-time updates use WebSocket subscriptions to broadcast events.Performance Optimizations
Snapshot System
Periodic snapshots reduce event replay time from O(n) to O(m) where m is much less than n
Indexed Queries
Database indexes on workspace_id, user_id, version, timestamp for fast queries
Lazy Loading
PDF pages and images load on-demand, not upfront
React Query
TanStack Query handles caching, deduplication, and background refetching
Related Documentation
- Tech Stack - Technologies and libraries used
- Contributing Guide - How to contribute to ThinkEx
- Workspace API - Workspace management API
- AI Tools API - AI integration API