DBA Handoff Summary
AI-Powered Photo Journaling iOS App - MVP Q1 2025Project: AI-Powered Photo Journaling iOS App Timeline: 12-week MVP (Q1 2025) Your Phase: Database Schema Design & Implementation Tech Stack: Supabase PostgreSQL (500MB free tier) Start Date: Ready to begin immediately Target Completion: Week 6 (Feb 4, 2025)
Executive Summary
You are responsible for designing and implementing the complete database schema for an AI-powered photo journaling iOS app built with Flutter. The app uses Supabase PostgreSQL as the primary database, with Supabase Storage for photos and Supabase Auth for authentication. Core Entities:- Users (authentication, profiles, subscriptions)
- Journal Entries (photos, text, emotions, AI prompts, metadata)
- Emotions (12-emotion taxonomy)
- Subscriptions (freemium model: 50 photos/month free, unlimited premium)
- Multi-tenancy (user data isolation)
- Row Level Security (RLS) policies for all tables
- Photo limit enforcement (50/month free tier)
- Search optimization (full-text search on journal entries)
- Performance targets (< 500ms query response, 60fps timeline scrolling)
Tech Stack
Database: Supabase PostgreSQL 15+ Version Control: Supabase Migrations (SQL files insupabase/migrations/)
ORM: None (direct SQL with Supabase Flutter client)
Storage: Supabase Storage (1GB free tier for photos)
Auth: Supabase Auth (JWT-based)
MCP Integration: You have access to Supabase MCP tools for database operations:
mcp__supabase__list_tables,mcp__supabase__execute_sqlmcp__supabase__list_migrations,mcp__supabase__apply_migrationmcp__supabase__list_projects,mcp__supabase__get_project
Database Schema Requirements
1. Users Table (users)
Extends Supabase Auth with profile data.
Columns:
monthly_photo_count: Reset to 0 on first day of each month (cron job or function trigger)last_photo_reset_date: Track when photo count was last resetdeleted_at: Soft delete (NULL = active, non-NULL = deleted, 30-day recovery window)
2. Journal Entries Table (journal_entries)
Core table storing all photo journal entries.
Columns:
search_vector: Full-text search onjournal_text+ai_prompt_used- Photo limit enforcement: Check
users.monthly_photo_countbefore INSERT (function trigger) photo_taken_at: Defaults tocreated_atif not provided (e.g., user doesn’t have EXIF data)
3. Emotions Table (emotions)
Reference table for 12-emotion taxonomy (read-only, seeded data).
Columns:
4. Entry Emotions Table (entry_emotions)
Junction table linking journal entries to emotions (many-to-many).
Columns:
5. AI Prompts Table (ai_prompts)
Stores all AI-generated prompts for each entry (3 prompts per entry).
Columns:
- 3 prompts generated per entry (stored before user selection)
was_selected: TRUE for the prompt user chose (max 1 per entry)- Used for analytics (which prompts are most popular, AI quality tracking)
6. Subscriptions Table (subscriptions)
Tracks Premium subscription details and Apple IAP receipts.
Columns:
- Receipt validation happens server-side (backend verifies with Apple)
statustransitions: trial → active → cancelled → expiredgrace_period: Apple retrying failed paymentbilling_retry: Payment method issue, subscription still active for limited time
Database Functions & Triggers
1. Photo Limit Enforcement Function
Prevents free users from creating >50 entries per month.2. Monthly Photo Count Reset Function
Resetsmonthly_photo_count to 0 on first day of each month (cron job).
3. Updated_At Trigger Function
Auto-updatesupdated_at timestamp on row changes.
Search & Filter Queries
Full-Text Search on Journal Entries
Filter by Emotions (OR logic)
Filter by Date Range
Combined Search + Filters
Performance Requirements
Target Metrics:- Timeline query (20 entries): < 100ms
- Search query: < 500ms
- Entry detail query: < 50ms
- Photo limit check: < 10ms (cached in application layer)
- Composite indexes for common query patterns (
idx_journal_entries_user_created) - GIN index for full-text search (
idx_journal_entries_search_vector) - RLS policies optimized (avoid subqueries when possible)
- Connection pooling (Supabase default: 15 connections)
- Query result caching in Flutter app (local SQLite cache via
sqflite)
Supabase Storage Schema
Bucket:journal-photos (public read, authenticated write)
File Structure:
Migration Strategy
Step 1: Create Migrations Directory
Step 2: Write Migration Files
Migration 1:20250110000001_create_users_table.sql
20250110000002_create_journal_entries_table.sql
20250110000003_create_emotions_tables.sql
20250110000004_create_ai_prompts_table.sql
20250110000005_create_subscriptions_table.sql
20250110000006_create_functions_and_triggers.sql
Step 3: Apply Migrations
Testing Checklist
Data Integrity Tests:
- Users table: Soft delete sets
deleted_at, does not cascade delete entries - Journal entries: Deleting entry cascades to
entry_emotionsandai_prompts - Emotions: Seed data contains exactly 12 emotions
- Entry emotions: Max 12 emotions per entry (no constraint, but app logic enforces)
- Subscriptions: Apple transaction IDs are unique
RLS Policy Tests:
- Users can only view/update their own profile
- Users can only view/insert/update/delete their own journal entries
- Users can view all emotions (public read-only)
- Users can only manage emotions for their own entries
- Users can only view their own subscriptions
Function/Trigger Tests:
- Photo limit: Free users blocked at 51st entry
- Photo limit: Premium users can create unlimited entries
- Monthly reset: Photo count resets on 1st of month
- Updated_at: Timestamp auto-updates on UPDATE
Performance Tests:
- Timeline query (20 entries): < 100ms
- Search query (keyword + filters): < 500ms
- Entry detail query: < 50ms
- Full-text search with GIN index works correctly
Storage Tests:
- Users can upload photos to own folder
- Users cannot access other users’ photos
- Photo URLs are publicly readable (signed URLs or public bucket)
Handoff to Backend Developer
After database schema is complete, hand off to @backend-developer with:- ✅ All migrations applied and tested
- ✅ Supabase project URL and anon key (for Flutter client)
- ✅ Database schema documentation (this document)
- ✅ Sample queries for common operations
- ✅ RLS policies verified and tested
- API endpoints for journal entries CRUD
- Photo upload to Supabase Storage
- AI prompt generation (OpenAI GPT-5 integration)
- Apple IAP receipt validation
- Data export (JSON + ZIP of photos)
Linear Ticket References
Create Linear tickets for each migration:- OVE-XX: Database Schema Design (this document)
- OVE-XX: Create Users Table Migration
- OVE-XX: Create Journal Entries Table Migration
- OVE-XX: Create Emotions Tables Migration
- OVE-XX: Create AI Prompts Table Migration
- OVE-XX: Create Subscriptions Table Migration
- OVE-XX: Create Functions & Triggers Migration
- OVE-XX: Test Database Performance
- OVE-XX: Setup Supabase Storage Policies
linear-integration skill to update progress as you complete each migration.
Resources
- Supabase Docs: https://supabase.com/docs
- Supabase MCP Tools: Available in this environment
- Database Schema Diagram: (TODO: Generate ERD diagram)
- PRD:
docs/product/prds/mvp-q1-2025-prd.md - Architecture:
docs/architecture/system-design.md,docs/architecture/tech-stack.md
Questions? Reach out to @software-architect or @backend-developer for clarification. Ready to start! 🚀