{
  "docVersion": 1,
  "id": "template-social-news-feed",
  "name": "Social news feed",
  "purpose": "Hybrid feed: fan-out-on-write into materialized timelines for normal users, read-time merge for celebrities, stampede-protected cache in front.",
  "nodes": {
    "postsvc": {
      "type": "server",
      "label": "Post Service",
      "x": 540,
      "y": 60,
      "runtime": "writes go here",
      "color": "blue"
    },
    "bus": {
      "type": "stream",
      "label": "PostCreated",
      "x": 800,
      "y": 60,
      "runtime": "at-least-once"
    },
    "fanout": {
      "type": "worker",
      "label": "Fanout Workers",
      "x": 1060,
      "y": 60,
      "runtime": "skip authors > 10k followers",
      "color": "blue",
      "patterns": [
        "autoscale"
      ],
      "notes": "Appends the post id into every follower's timeline — unless the author is a celebrity, in which case readers will pull it instead."
    },
    "timelines": {
      "type": "keyvalue",
      "label": "Timelines",
      "x": 1320,
      "y": 60,
      "runtime": "per-user · materialized",
      "patterns": [
        "sharded",
        "replicated"
      ]
    },
    "user": {
      "type": "user",
      "label": "Reader",
      "x": 20,
      "y": 300,
      "runtime": "opens the app"
    },
    "api": {
      "type": "bff",
      "label": "Feed API",
      "x": 280,
      "y": 300,
      "runtime": "read + write edge",
      "patterns": [
        "stateless"
      ]
    },
    "feedsvc": {
      "type": "server",
      "label": "Feed Builder",
      "x": 540,
      "y": 300,
      "runtime": "merge write-path + read-path",
      "color": "green",
      "notes": "Reads the precomputed timeline, pulls recent celebrity posts, merges by rank. The hybrid happens right here."
    },
    "cache": {
      "type": "cache",
      "label": "Feed Cache",
      "x": 800,
      "y": 300,
      "runtime": "hot feeds · early expiry",
      "notes": "Probabilistic early refresh: one request rebuilds before TTL death — the herd keeps hitting cache."
    },
    "push": {
      "type": "push",
      "label": "Notifications",
      "x": 1320,
      "y": 300,
      "runtime": "mentions · DMs"
    },
    "posts": {
      "type": "database",
      "label": "Posts Store",
      "x": 540,
      "y": 540,
      "runtime": "celebrity posts read here",
      "patterns": [
        "replicated"
      ]
    },
    "graph": {
      "type": "graphdb",
      "label": "Social Graph",
      "x": 1060,
      "y": 540,
      "runtime": "who follows whom"
    }
  },
  "edges": {
    "e1": {
      "from": "user",
      "to": "api",
      "fromSide": "right",
      "toSide": "left",
      "label": "GET /feed",
      "animated": true
    },
    "e2": {
      "from": "api",
      "to": "feedsvc",
      "fromSide": "right",
      "toSide": "left",
      "animated": true,
      "patterns": [
        "timeout"
      ]
    },
    "e3": {
      "from": "feedsvc",
      "to": "cache",
      "fromSide": "right",
      "toSide": "left",
      "label": "hot path",
      "animated": true,
      "patterns": [
        "cache-aside"
      ]
    },
    "e4": {
      "from": "feedsvc",
      "to": "timelines",
      "fromSide": "top",
      "toSide": "bottom",
      "label": "precomputed (normal authors)"
    },
    "e5": {
      "from": "feedsvc",
      "to": "posts",
      "fromSide": "bottom",
      "toSide": "top",
      "label": "pull celebrities at read time"
    },
    "e6": {
      "from": "api",
      "to": "postsvc",
      "fromSide": "top",
      "toSide": "bottom",
      "label": "POST /post"
    },
    "e7": {
      "from": "postsvc",
      "to": "bus",
      "fromSide": "right",
      "toSide": "left",
      "style": "dashed"
    },
    "e8": {
      "from": "bus",
      "to": "fanout",
      "fromSide": "right",
      "toSide": "left",
      "style": "dashed",
      "patterns": [
        "dedup"
      ]
    },
    "e9": {
      "from": "fanout",
      "to": "timelines",
      "fromSide": "right",
      "toSide": "left",
      "label": "append ×N followers",
      "patterns": [
        "batch"
      ]
    },
    "e10": {
      "from": "fanout",
      "to": "graph",
      "fromSide": "bottom",
      "toSide": "top",
      "label": "followers of author"
    },
    "e11": {
      "from": "fanout",
      "to": "push",
      "fromSide": "bottom",
      "toSide": "left",
      "label": "mentions",
      "style": "dashed"
    }
  },
  "boxes": {
    "b1": {
      "x": 500,
      "y": -20,
      "w": 970,
      "h": 230,
      "label": "write path — fan-out-on-write",
      "color": "blue",
      "opacity": 0.04
    },
    "b2": {
      "x": 500,
      "y": 250,
      "w": 450,
      "h": 230,
      "label": "read path — merge at read",
      "color": "green",
      "opacity": 0.04
    }
  },
  "texts": {
    "t1": {
      "x": 20,
      "y": 480,
      "text": "the hybrid rule:\nfollowers < 10k → write-time fanout\nfollowers ≥ 10k → read-time pull\none knob, both failure modes avoided",
      "color": "green",
      "size": 12
    }
  },
  "meta": {
    "createdAt": "2026-07-13T09:00:00.000Z",
    "modifiedAt": "2026-07-13T09:00:00.000Z"
  }
}