Skip to main content

DBA Handoff Summary

AI-Powered Photo Journaling iOS App - MVP Q1 2025
Project: 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)
Key Requirements:
  • 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 in supabase/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_sql
  • mcp__supabase__list_migrations, mcp__supabase__apply_migration
  • mcp__supabase__list_projects, mcp__supabase__get_project

Database Schema Requirements

1. Users Table (users)

Extends Supabase Auth with profile data. Columns:
Business Logic:
  • 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 reset
  • deleted_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:
Business Logic:
  • search_vector: Full-text search on journal_text + ai_prompt_used
  • Photo limit enforcement: Check users.monthly_photo_count before INSERT (function trigger)
  • photo_taken_at: Defaults to created_at if 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:
Business Logic:
  • 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:
Business Logic:
  • Receipt validation happens server-side (backend verifies with Apple)
  • status transitions: trial → active → cancelled → expired
  • grace_period: Apple retrying failed payment
  • billing_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

Resets monthly_photo_count to 0 on first day of each month (cron job).

3. Updated_At Trigger Function

Auto-updates updated_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)
Optimization Strategies:
  1. Composite indexes for common query patterns (idx_journal_entries_user_created)
  2. GIN index for full-text search (idx_journal_entries_search_vector)
  3. RLS policies optimized (avoid subqueries when possible)
  4. Connection pooling (Supabase default: 15 connections)
  5. Query result caching in Flutter app (local SQLite cache via sqflite)

Supabase Storage Schema

Bucket: journal-photos (public read, authenticated write) File Structure:
Storage Policies:

Migration Strategy

Step 1: Create Migrations Directory

Step 2: Write Migration Files

Migration 1: 20250110000001_create_users_table.sql
Migration 2: 20250110000002_create_journal_entries_table.sql
Migration 3: 20250110000003_create_emotions_tables.sql
Migration 4: 20250110000004_create_ai_prompts_table.sql
Migration 5: 20250110000005_create_subscriptions_table.sql
Migration 6: 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_emotions and ai_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:
  1. ✅ All migrations applied and tested
  2. ✅ Supabase project URL and anon key (for Flutter client)
  3. ✅ Database schema documentation (this document)
  4. ✅ Sample queries for common operations
  5. ✅ RLS policies verified and tested
Backend will implement:
  • 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
Use 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! 🚀