ExpertFeedsFan-outGraph dataModerationEventual consistency

Social Network

Create social graphs and feeds while controlling fan-out, abuse, and privacy.

Product overview

Support profiles, follows, posts, media, home feeds, interactions, notifications, and moderation while respecting privacy and high-degree graph behavior.

Main actors

Member, Moderator, Advertiser, Support.

Assumptions

  • Follow graph can be directed
  • Accounts may be private
  • Feed ordering starts chronological with policy filters
  • Counters are projections

Non-goals

  • Advertising auction
  • Opaque engagement ranking
  • End-to-end encrypted messaging
  • Replicating any named network

Requirements

Must-have

  • Profiles and follow lifecycle
  • Text/media posts
  • Home feed
  • Likes/comments
  • Block/report/moderation

Should-have

  • Private accounts
  • Mentions
  • Notifications
  • Post edit history

Future scope

  • Groups
  • Federation
  • Recommendations
  • Direct messages

Non-functional decisions

ConcernDecision
AvailabilityPost writes survive feed-worker outages; feeds may be delayed with freshness shown internally.
ConsistencyPrivacy/block/follow acceptance is authoritative; feeds and counters converge.
ScalabilityHybrid fan-out prevents one high-degree author from overwhelming write fan-out.
SafetyModeration, reports, rate limits, and appeals are first-class workflows.
PrivacyAudience is checked again when hydrating feed candidates.

User roles

RoleAccess boundary
MemberOwn profile/posts; authorized social content
ModeratorQueued reports/content with audited decisions
SupportAccount recovery metadata, not unrestricted content
Safety adminPolicy configuration and restricted appeals

Core workflows

Publish
Authorize account → Finalize scanned media → Create post → Write durable post/outbox → Fan out to ordinary followers → Leave celebrity posts for read-time merge → Notify mentions
Read feed
Load feed cursor → Merge precomputed inbox with followed high-fanout authors → Filter blocks/privacy/deletions → Hydrate authorized posts → Return stable cursor

Domain model

EntitiesRelationship
Account → FollowEdgeDirected requested/accepted relationship
Account → Post → MediaAuthor owns post; media publish follows scan
Post → Reaction / CommentInteractions have uniqueness/audience checks
Account → FeedItemMaterialized candidate with source post
Report → ModerationDecisionEvidence and reversible policy action

Database design

PostgreSQL remains the transactional source of truth. Add specialized stores only for the workloads called out below.

follows — Directed social graph

follows ------- follower_id UUID followed_id UUID status TEXT created_at TIMESTAMPTZ Constraints: PK(follower_id, followed_id); follower_id <> followed_id Indexes: (followed_id, status, follower_id); (follower_id, status)

posts — Authoritative post

posts ----- id UUID PK author_id UUID FK body TEXT audience TEXT status TEXT created_at TIMESTAMPTZ version INT Constraints: bounded body length Indexes: (author_id, created_at DESC, id DESC); (status, created_at)

feed_items — Per-user feed candidates

feed_items ---------- user_id UUID post_id UUID author_id UUID sort_key BIGINT created_at TIMESTAMPTZ Constraints: PK(user_id, post_id) Indexes: (user_id, sort_key DESC, post_id DESC)

moderation_actions — Audited policy decisions

moderation_actions ------------------ id UUID PK target_type TEXT target_id UUID actor_id UUID policy TEXT action TEXT reason TEXT created_at TIMESTAMPTZ Constraints: append-only Indexes: (target_type, target_id, created_at)

Transaction and retention boundaries

  • Post and outbox commit atomically
  • Accept private follow conditionally changes requested edge
  • Block transaction deletes/conflicts follow edges and increments privacy epoch
  • Hard delete media only after tombstone propagation/retention; moderation actions remain audited

API design

POST /v1/posts GET /v1/feed?cursor=… POST /v1/accounts/:id/follows POST /v1/posts/:id/reactions POST /v1/reports DELETE /v1/posts/:id
EndpointPurpose and permissionIdempotency / status
POST /v1/postsPublish post with finalized media ids
Member
Client mutation id
201, 422, 429
GET /v1/feed?cursor=…Stable keyset feed
Member
Not required
200
POST /v1/accounts/:id/followsFollow or request follow
Member; block rules
Unique edge
200, 403, 409
POST /v1/posts/:id/reactionsSet reaction
Authorized viewer
Upsert by user/post/type
200, 403
POST /v1/reportsReport content/account
Member with rate limit
Client report id
201, 429
DELETE /v1/posts/:idTombstone own post
Author or policy-authorized moderator
Idempotent
204, 403

List endpoints use opaque keyset cursors unless a small bounded collection is explicit. Unknown objects return 404 where 403 would leak existence.

Architecture

System context

Social Network system context
Social Network system context

Main application architecture

Social Network application architecture
Social Network application architecture

Critical workflow sequence

Social Network workflow sequence
Social Network workflow sequence

Event model

EventProducer → consumersDelivery
PostPublishedPost API → Feed fan-out, search, mention notificationOutbox; feed unique(user,post)
PrivacyChangedGraph API → Feed repair, cache invalidationEpoch/version; hydration remains final guard
ContentRemovedModeration → Feed/search/media purgeHigh priority, replayable tombstone

Failure modes

Fan-out lag

Failure
Post exists but followers do not see it yet.

Mitigation
Durable outbox/queue, lag metric, and author-profile read remains authoritative.

Celebrity explosion

Failure
Millions of follower writes per post.

Mitigation
Classify high-fanout authors and merge their posts at read time.

Privacy race

Failure
User becomes private/blocks after item entered feed.

Mitigation
Hydration rechecks current audience/block epoch before returning content.

Duplicate like

Failure
Client retries reaction.

Mitigation
Unique user/post/type upsert; counters consume change events idempotently.

Deleted post remains

Failure
Stale cache/feed references tombstoned post.

Mitigation
Hydration omits tombstones; removal event and short negative cache accelerate cleanup.

Moderation retry

Failure
Same action is applied twice.

Mitigation
Decision idempotency id and state-aware reversible actions.

Consistency and concurrency

Strong consistency / transactions

  • Account/block/privacy state
  • Post ownership
  • follow edge
  • moderation decision

Eventual convergence

  • Home feed
  • reaction/follower counts
  • search
  • notifications

Concurrency controls

  • Unique graph/reaction constraints absorb retries
  • Privacy epoch invalidates cached audience decisions
  • Post version protects edits
  • Feed insertion is idempotent; no per-user distributed lock

Caching and background work

Safe to cache

  • Public profile/post with audience epoch
  • Media via CDN
  • Feed page briefly per user/version

Never a cache authority

  • Block decision without epoch
  • Unreviewed uploads
  • Moderator evidence in shared caches

Cache public immutable media aggressively. Audience-sensitive objects include policy epoch and are reauthorized on hydration.

Background jobs

  • Feed fan-out/repair
  • Media scan/transcode
  • Counter aggregation
  • Search indexing/removal
  • Notifications
  • Abuse classification and retention purge

Security boundaries

Security claims here are design controls, not certification. Threat modeling and jurisdiction-specific review remain required.

  • Object-level audience/block checks
  • Sanitize text/links and proxy or validate remote previews against SSRF
  • Scan and re-encode uploads
  • Rate-limit follows, posts, comments, reports by behavioral signals
  • Moderator access/actions are least-privilege and audited
  • Prevent account enumeration in private/recovery flows
  • Deletion and export honor retention/legal constraints explicitly

Observability

Logs

  • Post/follow/block/moderation transitions with policy version and safe target ids
  • Feed fan-out event, recipient batch, high-fanout classification, and dedupe result
  • Audience-filter rejection reason without copying private post bodies

Metrics

  • Post-to-feed availability and fan-out queue age by author class
  • Feed hydration drop rate from block/privacy/deletion checks
  • Reaction/counter projection lag and duplicate-event rate
  • Report backlog age, moderation turnaround, and media quarantine failures

Traces

  • Trace publish through durable post, fan-out classification, feed insertion, and mention notification
  • Trace feed read through candidate merge, authorization filtering, and hydration

Alerts

  • Removal events fail to clear feed/search projections
  • Report backlog breaches safety response target
  • Celebrity fan-out saturates queues used by ordinary authors

Testing strategy

LayerWhat it proves
UnitAudience policy matrix, cursors, post state, fan-out classifier
IntegrationGraph uniqueness, outbox, tombstone filters, privacy epoch
ContractVersioned feed and moderation events
ConcurrencyFollow/block races and duplicate reactions
FailureFan-out outage, stale cache, deletion replay
LoadSkewed follower graph and celebrity post bursts

Do not mock PostgreSQL constraints, transaction isolation, object-store semantics, or provider contract fixtures in the tests intended to prove those boundaries.

Deployment evolution

Stage 1
Modular API, PostgreSQL, queue/workers, object storage/CDN, Redis
Stage 2
Feed service/store, search, media pipeline, realtime/notification workers
Stage 3
Account/graph and feed partitions; hybrid fan-out; regional media delivery

Scale evolution

These are architecture scenarios, not throughput claims. Each transition should follow measured workload and reliability pressure.

ScenarioArchitecture may evolve towardDecision rule
1K usersOne API, PostgreSQL for accounts, follows, and posts, timelines built at read time, a media worker, object storageBuild timelines on read while follower counts are small; privacy and block checks run on every read
100K usersIntroduce hybrid fan-out, separate feed store/search/media workers, read replicas for profilesFan out on write for ordinary accounts and on read for high-follower accounts once timeline reads dominate
1M+ usersPartition graph/posts by account and feeds by recipient; use explicit routing and accept eventual cross-partition timelinesAccept eventually consistent cross-partition timelines, but never eventually consistent blocks or privacy changes