1 User / Profile Separation
The Problem
Storing all user data in one table couples authentication concerns (email, password hash, MFA) with profile concerns (bio, avatar, preferences). This bloats the users table and leaks auth fields into profile APIs.
The Solution
Split into users (auth) and profiles (public/personal data) with a 1:1 relationship.
CREATE TABLE users ( id SERIAL PRIMARY KEY, email VARCHAR(255) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, email_verified_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE profiles ( id SERIAL PRIMARY KEY, user_id INT NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE, display_name VARCHAR(100), bio TEXT, avatar_url VARCHAR(500), timezone VARCHAR(50) DEFAULT 'UTC', updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX idx_profiles_user_id ON profiles(user_id);
ā When to use
- Public profiles are queried often
- Auth fields should not leak to profile APIs
- Different caching strategies for auth vs profile
ā ļø Trade-offs
- Extra JOIN on every profile fetch
- Slightly more complex registration flow