Skip to content

URL shortener system design

vendor-neutral `generic:` icons and product logos, from CDN, load balancer and API servers to Redis, PostgreSQL and a Kafka, Flink and ClickHouse click pipeline.

logossitabler11 nodes1 groups11 edges
Open full size
URL shortener system design with neutral icons and product logosURL shortener system design with neutral icons and product logosURL shortener system design with neutral icons and product logosURL shortener system design with neutral icons and product logos
system-design-url-shortener.yamlOn GitHub
# yaml-language-server: $schema=../references/diagram.schema.json
title: URL shortener system design
subtitle: Redirects are served from the CDN and a Redis cache in front of PostgreSQL; every click streams through Kafka into an analytics pipeline
direction: down
layout: { spacing: compact }

legend:
  solid: Request or query
  dashed: Asynchronous event or stream

groups:
  analytics: { type: zone, label: Click analytics pipeline }

nodes:
  clients: { icon: generic:users, label: Clients, note: Browsers and apps }
  cdn: { icon: generic:cdn, label: CDN, note: Caches popular redirects }
  lb: { icon: generic:load-balancer, label: Load balancer }
  api: { icon: generic:servers, label: API servers, note: Stateless, badge: x3 }
  cache: { icon: logos:redis, label: Redis, note: Hot short codes }
  db: { icon: logos:postgresql, label: PostgreSQL, note: Sharded by short code }
  ids: { icon: tabler:hash, label: ID generator, note: Base62 key ranges }
  events: { icon: logos:kafka-icon, label: Kafka, note: clicks topic, in: analytics }
  stream: { icon: logos:apache-flink-icon, label: Apache Flink, note: Counts per link, in: analytics }
  olap: { icon: si:clickhouse, label: ClickHouse, note: Click analytics, in: analytics }
  dashboard: { icon: generic:dashboard, label: Dashboards, in: analytics }

edges:
  - clients -> cdn: GET /abc123
  # Creating a link skips the CDN and goes straight to the load balancer.
  - clients -> lb: POST /links
  - cdn -> lb: cache miss
  - lb -> api
  # All three data-store edges are labelled, so Redis, PostgreSQL and the ID generator share one row; Kafka sits in the analytics group.
  - api -> cache: lookup, fill on miss
  - api -> db: read on miss, insert new
  - api -> ids: new link
  - api --> events: click event
  # Redirects the CDN answers never reach the API, so the CDN ships its own logs.
  - cdn --> events: edge logs
  - events --> stream --> olap
  # Written in layout order with the arrow flipped: dashboards query ClickHouse.
  - { from: olap, to: dashboard, arrow: start, label: query }

Start from this one

Copies the spec to diagram.yaml, then renders every format.

$ orrery init system-design-url-shortener$ orrery render diagram.yaml -f all --theme both