{
  "docVersion": 1,
  "id": "template-ecommerce-search-platform",
  "name": "Search — CQRS + fan-out",
  "purpose": "Catalog writes flow via CDC into a sharded inverted index; the search API scatter-gathers across shards with hedged requests and re-ranks candidates with a model.",
  "nodes": {
    "shopper": {
      "type": "user",
      "label": "Shopper",
      "x": 0,
      "y": 240,
      "runtime": "types 'red running shoes'"
    },
    "api": {
      "type": "bff",
      "label": "Search API",
      "x": 260,
      "y": 240,
      "runtime": "scatter-gather coordinator",
      "color": "blue",
      "patterns": [
        "stateless",
        "autoscale"
      ]
    },
    "s1": {
      "type": "search",
      "label": "Shard 1",
      "x": 520,
      "y": 60,
      "runtime": "terms a–h",
      "patterns": [
        "replicated"
      ]
    },
    "s2": {
      "type": "search",
      "label": "Shard 2",
      "x": 520,
      "y": 240,
      "runtime": "terms i–q",
      "patterns": [
        "replicated"
      ]
    },
    "s3": {
      "type": "search",
      "label": "Shard 3",
      "x": 520,
      "y": 420,
      "runtime": "terms r–z",
      "patterns": [
        "replicated"
      ]
    },
    "rank": {
      "type": "agent",
      "label": "Re-ranker",
      "x": 780,
      "y": 240,
      "runtime": "ML · top-200 → top-20",
      "color": "purple",
      "notes": "Recall is the index's job, precision is the model's. Re-ranking 200 candidates costs ms; ranking a million would cost seconds."
    },
    "cat": {
      "type": "k8s",
      "label": "Catalog Service",
      "x": 520,
      "y": 620,
      "runtime": "the write side"
    },
    "db": {
      "type": "database",
      "label": "Catalog DB",
      "x": 780,
      "y": 620,
      "runtime": "source of truth"
    },
    "cdc": {
      "type": "stream",
      "label": "Change Stream",
      "x": 1040,
      "y": 480,
      "runtime": "CDC → index upserts",
      "color": "purple"
    },
    "idxr": {
      "type": "worker",
      "label": "Indexer",
      "x": 1040,
      "y": 240,
      "runtime": "analyze · tokenize · upsert"
    }
  },
  "edges": {
    "e1": {
      "from": "shopper",
      "to": "api",
      "fromSide": "right",
      "toSide": "left",
      "label": "query",
      "animated": true,
      "patterns": [
        "timeout"
      ]
    },
    "e2": {
      "from": "api",
      "to": "s1",
      "fromSide": "right",
      "toSide": "left",
      "label": "fan-out",
      "animated": true,
      "patterns": [
        "hedged"
      ]
    },
    "e3": {
      "from": "api",
      "to": "s2",
      "fromSide": "right",
      "toSide": "left",
      "patterns": [
        "hedged"
      ]
    },
    "e4": {
      "from": "api",
      "to": "s3",
      "fromSide": "right",
      "toSide": "left",
      "patterns": [
        "hedged"
      ]
    },
    "e5": {
      "from": "s2",
      "to": "rank",
      "fromSide": "right",
      "toSide": "left",
      "label": "gather top-200",
      "animated": true
    },
    "e6": {
      "from": "rank",
      "to": "api",
      "fromSide": "top",
      "toSide": "top",
      "label": "top-20, re-ranked",
      "style": "dashed"
    },
    "e7": {
      "from": "cat",
      "to": "db",
      "fromSide": "right",
      "toSide": "left",
      "label": "writes"
    },
    "e8": {
      "from": "db",
      "to": "cdc",
      "fromSide": "right",
      "toSide": "left",
      "label": "CDC",
      "style": "dashed"
    },
    "e9": {
      "from": "cdc",
      "to": "idxr",
      "fromSide": "top",
      "toSide": "bottom",
      "label": "changes",
      "style": "dashed"
    },
    "e10": {
      "from": "idxr",
      "to": "s2",
      "fromSide": "left",
      "toSide": "right",
      "label": "upsert · idempotent",
      "patterns": [
        "idempotent",
        "batch"
      ]
    }
  },
  "boxes": {
    "b1": {
      "x": 490,
      "y": -40,
      "w": 220,
      "h": 620,
      "label": "read model — disposable, rebuildable",
      "color": "green",
      "opacity": 0.04
    },
    "b2": {
      "x": 490,
      "y": 560,
      "w": 560,
      "h": 220,
      "label": "write model — boring on purpose",
      "color": "blue",
      "opacity": 0.04
    }
  },
  "texts": {
    "t1": {
      "x": 0,
      "y": 460,
      "text": "hedging: if a shard hasn't answered by p95,\nfire a backup request to its replica and take\nthe first response. tail latency, tamed.",
      "color": "amber",
      "size": 12
    }
  },
  "meta": {
    "createdAt": "2026-07-13T09:00:00.000Z",
    "modifiedAt": "2026-07-13T09:00:00.000Z"
  }
}