BlogPricing
PostgreSQL

Activity feed / timeline database schema

An activity-stream schema (actor + verb + object + target) with a fan-out-on-write timeline table. Designed for follow-based feeds at scale.

Start for free
Database schema previewActivity feed / timeline database schema
Recreate this in Raltey

Copy this prompt

Paste this into Raltey's AI agent to generate the same diagram, then tweak it on the canvas.

Prompt
Design a PostgreSQL database schema for: Activity feed / timeline database schema.

An activity-stream schema (actor + verb + object + target) with a fan-out-on-write timeline table. Designed for follow-based feeds at scale.

Include these tables: activity, timeline_entry, follow.

Use proper primary keys, foreign keys, and indexes. Export SQL DDL for PostgreSQL.

Context

  • - Activity model: actor performs a verb on an object (optionally targeting another object).
  • - Timeline rows are denormalized per recipient to keep reads cheap.
  • - Celebrity accounts skip fan-out-on-write and compute at read time.

Patterns

  • - Activity Stream 2.0-style schema (actor, verb, object, target).
  • - Fan-out-on-write for normal accounts.
  • - Hybrid: skip fan-out for celebrities, merge at read time.
Tables

The schema, table by table

activity

Canonical activity record - the source of truth.

DBML - activity
Table activity {
  id          uuid pk
  actor_id    uuid [ref: > user_account.id, not null]
  verb        text not null   // followed, liked, commented
  object_type text not null
  object_id   uuid not null
  target_type text
  target_id   uuid
  created_at  timestamptz default `now()`
}

timeline_entry

One row per recipient per activity (fan-out on write).

DBML - timeline_entry
Table timeline_entry {
  user_id     uuid [ref: > user_account.id]
  activity_id uuid [ref: > activity.id]
  created_at  timestamptz not null
  pk (user_id, created_at, activity_id)
}

follow

DBML - follow
Table follow {
  follower_id uuid [ref: > user_account.id]
  followee_id uuid [ref: > user_account.id]
  pk (follower_id, followee_id)
}

Relationships

FromToType
user_accountactivity1:N
activitytimeline_entry1:N
user_accounttimeline_entry1:N
user_accountfollow1:N

Notes from the field

Frequently asked

Is fan-out-on-write always the right call?

No. It is great for the long tail but breaks for celebrity accounts where one write fans out to millions of timelines. Hybrid models pull celebrity activities at read time instead.

Why duplicate created_at on timeline_entry?

Pagination orders by it. Including it in the composite PK makes the index optimal and keeps the join-free read path fast.

More templates

Open this template in Raltey

Edit it on the canvas, ask the AI agent to extend it, then share or export.

Start for free