🗄️ Data Model

Table of contents

PostgreSQL via EF Core 10, snake_case columns/tables (EFCore.NamingConventions), UUID v7 primary keys (Guid.CreateVersion7() — time-ordered, index-friendly).

Entities

Table Purpose
newsletters One row per ingested email; email_hash (SHA-256, unique) drives idempotency
links One row per distinct article URL; url_hash (SHA-256 of the normalized URL, unique) drives dedup
authors One row per attributed author; unique on medium_handle and custom_domain (both nullable)
newsletter_links Join: which newsletters a link appeared in — “sent in N different newsletters”
link_authors Join: which authors are attributed to a link
users Single-admin auth; bcrypt password_hash
user_preferences Learned author/keyword weights, clamped to [-1.00, 1.00] via a check constraint

Key constraints

Seed data

The initial migration seeds one admin user: admin / ChangeMe123!. There is no self-service password-change endpoint by design — see Deployment §10 for the rotation procedure.