{
  "docVersion": 1,
  "id": "template-notification-service",
  "name": "Notification service",
  "purpose": "Producers emit intents; a router expands recipients against preferences; priority + delayed queues feed senders with a dedup window, provider adapters and a DLQ.",
  "nodes": {
    "orders": {
      "type": "server",
      "label": "Order Service",
      "x": 20,
      "y": 110,
      "runtime": "OrderShipped"
    },
    "marketing": {
      "type": "server",
      "label": "Campaign Tool",
      "x": 20,
      "y": 380,
      "runtime": "2M-user blast"
    },
    "ingest": {
      "type": "gateway",
      "label": "Notify API",
      "x": 280,
      "y": 240,
      "runtime": "intents in",
      "patterns": [
        "stateless"
      ]
    },
    "router": {
      "type": "worker",
      "label": "Router",
      "x": 540,
      "y": 240,
      "runtime": "expand · prefs · quiet hours",
      "color": "blue",
      "notes": "Turns one intent into concrete (user, channel, locale, when) sends — the only place preference logic lives."
    },
    "prefs": {
      "type": "database",
      "label": "Preferences",
      "x": 540,
      "y": 480,
      "runtime": "channels · quiet hours · locales"
    },
    "pq": {
      "type": "queue",
      "label": "Priority Queue",
      "x": 800,
      "y": 110,
      "runtime": "transactional first",
      "color": "green"
    },
    "delayq": {
      "type": "queue",
      "label": "Scheduled Queue",
      "x": 800,
      "y": 380,
      "runtime": "digests · quiet-hours holds"
    },
    "dedup": {
      "type": "keyvalue",
      "label": "Dedup Window",
      "x": 1060,
      "y": 20,
      "runtime": "event+user · 24h TTL"
    },
    "sender": {
      "type": "worker",
      "label": "Senders",
      "x": 1060,
      "y": 240,
      "runtime": "provider adapters",
      "patterns": [
        "autoscale"
      ],
      "notes": "Checks the dedup window, then hands to the channel adapter. Provider outages trip per-channel circuit breakers."
    },
    "dlq": {
      "type": "dlq",
      "label": "Dead Letters",
      "x": 1060,
      "y": 480,
      "runtime": "after 5 attempts",
      "color": "amber"
    },
    "email": {
      "type": "email",
      "label": "Email",
      "x": 1320,
      "y": 60,
      "runtime": "SES / Resend"
    },
    "sms": {
      "type": "sms",
      "label": "SMS",
      "x": 1320,
      "y": 240,
      "runtime": "Twilio"
    },
    "push": {
      "type": "push",
      "label": "Push",
      "x": 1320,
      "y": 420,
      "runtime": "APNs / FCM"
    }
  },
  "edges": {
    "e1": {
      "from": "orders",
      "to": "ingest",
      "fromSide": "right",
      "toSide": "left",
      "label": "OrderShipped",
      "animated": true
    },
    "e2": {
      "from": "marketing",
      "to": "ingest",
      "fromSide": "right",
      "toSide": "left",
      "label": "campaign",
      "style": "dashed",
      "patterns": [
        "rate-limit"
      ]
    },
    "e3": {
      "from": "ingest",
      "to": "router",
      "fromSide": "right",
      "toSide": "left",
      "animated": true
    },
    "e4": {
      "from": "router",
      "to": "prefs",
      "fromSide": "bottom",
      "toSide": "top",
      "label": "expand + filter"
    },
    "e5": {
      "from": "router",
      "to": "pq",
      "fromSide": "right",
      "toSide": "left",
      "label": "transactional",
      "animated": true
    },
    "e6": {
      "from": "router",
      "to": "delayq",
      "fromSide": "right",
      "toSide": "left",
      "label": "digest @ 9am local",
      "style": "dashed"
    },
    "e7": {
      "from": "pq",
      "to": "sender",
      "fromSide": "right",
      "toSide": "left",
      "patterns": [
        "ordered"
      ]
    },
    "e8": {
      "from": "delayq",
      "to": "sender",
      "fromSide": "right",
      "toSide": "left",
      "style": "dashed"
    },
    "e9": {
      "from": "sender",
      "to": "dedup",
      "fromSide": "top",
      "toSide": "bottom",
      "label": "seen? skip",
      "patterns": [
        "dedup"
      ]
    },
    "e10": {
      "from": "sender",
      "to": "email",
      "fromSide": "right",
      "toSide": "left",
      "patterns": [
        "retry",
        "timeout"
      ]
    },
    "e11": {
      "from": "sender",
      "to": "sms",
      "fromSide": "right",
      "toSide": "left",
      "label": "adapters · per-channel breakers",
      "patterns": [
        "circuit-breaker"
      ]
    },
    "e12": {
      "from": "sender",
      "to": "push",
      "fromSide": "right",
      "toSide": "left"
    },
    "e13": {
      "from": "sender",
      "to": "dlq",
      "fromSide": "bottom",
      "toSide": "top",
      "label": "poison → park",
      "style": "dashed"
    }
  },
  "boxes": {
    "b1": {
      "x": 760,
      "y": -50,
      "w": 450,
      "h": 640,
      "label": "the line — priority means OTPs never wait",
      "color": "green",
      "opacity": 0.04
    }
  },
  "texts": {
    "t1": {
      "x": 20,
      "y": 550,
      "text": "why a priority queue, not two services:\none pipeline = one dedup window, one\npreference model, one audit trail —\npriority is a queue property, not a fork",
      "color": "green",
      "size": 12
    }
  },
  "meta": {
    "createdAt": "2026-07-13T09:00:00.000Z",
    "modifiedAt": "2026-07-13T09:00:00.000Z"
  }
}