Architecture One-Pager
Solution Architecture v1.0 · Microsoft Azure · Platform Architecture · 2026-09 · 23 views · 16 architecture decision records
URL Shortener & Link Management Service · Solution Architecture v1.0 · Microsoft Azure · Platform Architecture · 2026-09 · 23 views · 16 architecture decision records
The redirect plane and the management plane are separate systems joined only by one-way propagation, and the redirect plane never writes. Everything the platform wants from a click happens after the redirect has already been sent.
A short link is the most disposable-looking artefact in software and one of the least changeable. It goes into an email, onto a poster, into a QR code on a badge, and from that moment the platform cannot reach it. What it can change is where the code points — and that is the product. So the system has to do three things that pull against each other: answer 150,000 lookups a second from everywhere on earth in single-digit milliseconds; let an owner repoint or kill a code that is already printed on ten thousand cards, and have that take effect globally in under a minute; and count every click accurately enough to bill on, without ever putting a write in front of a read that outnumbers writes 500 to 1. The obvious architecture — one service, one database, look up the code, increment the counter, redirect — satisfies all three on a whiteboard and none of them in production.
Codes are claimed once, by a conditional create against a strongly consistent reservation index, and are then immutable for the life of the platform: never reassigned, never reissued, never reclaimed. The link record is written only by the management plane, in one write region, and is the single authority for what a code means. Resolution reads a projection of it, never the record itself on the hot path: Front Door terminates TLS at the nearest of 190+ PoPs for both the shared and the 12,000 branded domains, caches the redirect for 30 seconds, and forwards a miss to the nearest of twelve stateless read regions, where a resolver checks the deny-set first, coalesces concurrent misses, then walks an in-process LRU, a regional Redis hot set and an in-region Cosmos replica. Two propagation channels cross from management into resolution, with two published bounds: a compact revocation delta that reaches every region within 60 seconds, and a full projection off the change feed within five minutes. Clicks are counted from the edge access log rather than from application code, which is what lets a redirect be cached without losing the click, and which keeps every write off the read path. Destinations are screened before a code is issued and re-screened afterwards, because a destination that was clean in March can be phishing in June.
What it is, and what it is not
- A read-only redirect plane fed by one-way propagation — not one service that reads and writes the link table on the click path, which is correct in every steady-state diagram and makes the write store carry the read rate.
- Clicks counted from the edge access log — not a counter incremented by the resolver, which forces every click to reach application code and makes caching and analytics mutually exclusive.
- Revocation on its own fast channel with a published 60-second bound — not one propagation path sized for the tightest deadline, which pays the urgent cost on every routine edit.
- A code that is identity, and a destination that is state — not a reusable code, which eventually resolves a 2024 QR code to a stranger's site.
- Exactly one strongly consistent operation, and it is alias uniqueness — not strong consistency on the read path, which would make a global redirect a coordination problem.
- A 302 with a 30-second cache lifetime — not a permanent redirect, which is faster, cheaper, and makes both revocation and counting impossible for as long as a browser chooses to remember it.
- Four distinct terminal states the clicker can act on — not a generic 404 for unknown, expired, deactivated and blocked alike, which turns every dead link into a support ticket.
- Screening as a product requirement — not an abuse process bolted on after the first phishing campaign, by which time the domain's reputation is already the problem.
The decisions that are the architecture
- Two planes, one-way propagation (ADR-01) — The management plane is the only writer; the redirect plane reads a projection and a deny-set and holds no write credential. A four-hour management outage costs zero redirects.
- Revocation has its own channel (ADR-02) — Stop instructions travel as a compact delta with a 60-second bound, independent of the five-minute projection. Urgency is bought where it is needed and nowhere else.
- Codes are random, then immutable (ADR-03) — Seven unguessable base62 characters, claimed by conditional create, never reassigned and never reissued after retirement. (ADR-04 carries the rest.)
- One strongly consistent operation (ADR-05) — Alias uniqueness. Everything else on the read path is eventually consistent, with its staleness published as a number rather than implied as instant. (ADR-15 carries the rest.)
- 302 with a 30-second cache (ADR-08) — Slower and more expensive than a permanent redirect, and the only choice that keeps a printed link both countable and killable.
- The redirect path never writes (ADR-10) — Click events come from the edge access log after the response has left, durably buffered, with a 0.05% loss budget and daily reconciliation. (ADR-11 carries the rest.)
- Bots are classified, not blocked (ADR-12) — An assumed 18% of traffic is unfurlers and scanners. They are served, tagged, excluded from customer counts and from tenant quotas, and kept in the raw data.
- Screening is continuous, and blocking is loud (ADR-13) — Destinations are checked before a code is issued and re-checked afterwards; a blocked link gets a platform interstitial that names the reason, never a silent 404. (ADR-14 carries the rest.)
- Branded domains are the platform's problem (ADR-16) — Twelve thousand customer domains mean certificate renewal is an availability risk the platform owns and alarms on before expiry, not a customer responsibility.
Why this should still be right in ten years
A link platform outlives everything around it, because its output is printed, embedded and forwarded by people who will never contact it again. These are the properties that should survive a change of scale, of cloud, and of the team.
- Code immutability is the promise that ages best. Every other decision here can be revisited. This one cannot, because the evidence of its violation arrives years later as a QR code on a poster resolving to a stranger's site. It depends on no technology and costs almost nothing to keep — which is exactly why a future team, looking at a table of desirable retired aliases, will be tempted to break it.
- The plane boundary survives the cloud. ADR-01 says the redirect path reads a projection and never writes. That holds if Front Door is replaced by a programmable edge, if Cosmos DB is replaced by anything with a point read, and if the whole thing moves to another provider. What would break it is one feature request — real-time click counts on the redirect response — which is why the boundary is drawn in views 08 and 14 rather than left as a convention.
- Published staleness is a contract, not a caveat. The 60-second revocation bound and the five-minute edit bound will both be argued about, and both may tighten as technology improves. What must not change is that the platform states a number instead of implying immediacy, because support answers, customer expectations and abuse response all derive from that number being sayable in one sentence.
- Counting from the edge log is a structural choice. It is the reason caching and analytics are not in conflict, and it will still be the reason on any edge platform that produces a request log. The fragile version of this decision is counting in application code, which looks identical on a dashboard and quietly couples the cache hit rate to the accuracy of the numbers customers are billed on.
- Resolution placement is the decision most likely to be redone. ADR-07 puts the lookup in twelve regional resolvers because Azure offers no general-purpose replicated key-value store at the PoP. That is a statement about 2026, not about architecture. When a programmable edge with a replicated store is available and affordable, the lookup should move — and nothing above needs to change when it does, which is the test of whether the boundary was drawn in the right place.
Non-functional targets
Every number here is a stated assumption from the requirement, chosen to be argued with. The right-hand column names the view where the mechanism that meets it is drawn.
| Quality | Target | How it is met | View |
|---|---|---|---|
| Redirect availability | ≥ 99.99% monthly per region | Anycast edge with a 30 s response cache, twelve stateless read regions, and no synchronous dependency on the management plane or the click pipeline. | 18 |
| Redirect latency | p50 ≤ 8 ms, p99 ≤ 40 ms; ≤ 120 ms p99 on a cold read | Edge cache, then an in-process LRU, a regional Redis hot set and an in-region Cosmos replica, with concurrent misses coalesced per code. | 14 |
| Correctness | Zero codes resolving to a destination they were not assigned | Conditional create against a strongly consistent reservation index, permanent code retirement, and a continuous synthetic wrong-destination probe. | 15 |
| Revocation | Effective globally ≤ 30 s p95, ≤ 60 s p99 | A compact revocation delta on its own channel, pulled every 5 s, matched before any cache layer; the 30 s edge TTL bounds the worst case. | 16 |
| Edit propagation | ≤ 5 min p99 globally; ≤ 1 s p99 in the creating region | Cosmos change feed to a projection worker in each read region, pre-warming the hot set at creation. | 13 |
| Throughput | 150,000 redirects/s steady, 600,000/s peak for 5 min | 97% served at the edge; resolver fleets scaled on requests per second with 4× headroom for the assumed burst. | 18 |
| Hot key | A single code at 80,000/s without degrading others | In-process LRU ahead of the shared cache, request coalescing, and a hot-set distribution that does not concentrate on one cache shard. | 09 |
| Creation | p99 ≤ 250 ms; 10,000-link import ≤ 60 s p99 | Single-region conditional create plus a durable write, with bulk import as an asynchronous job on separate capacity. | 15 |
| Analytics freshness | Click visible ≤ 60 s p95, ≤ 5 min p99 | Edge access log to Event Hubs to Azure Data Explorer, with rollups as materialised views. | 17 |
| Analytics accuracy | Loss ≤ 0.05%; counts within ±0.5%; uniques ±2%, labelled | Durable buffering at the point of collection and daily reconciliation against the edge counters. | 17 |
| Durability | Link table RPO 0, read RTO ≤ 60 s, write RTO ≤ 5 min | Cosmos DB multi-region replication with a single write region and a named failover target. | 18 |
| Rebuildability | All derived state rebuilt ≤ 30 min, no operator input | Hot set, negative cache and deny-set are all projections of the link table, rebuilt from the change feed; drilled monthly. | 19 |
| Cost | ≤ $0.05 per million redirects; alarm above $0.08 | Edge hit rate ≥ 97% as the primary lever, raw click retention capped at 90 days, bot traffic metered separately. | 20 |
Scope
In scope
- Code allocation, custom aliases, reserved-name refusal and permanent code retirement.
- The redirect path: edge termination for shared and branded domains, resolution, routing rules, parameter merge and the four terminal states.
- Link lifecycle: destination editing, scheduled expiry, deactivation, reactivation and the audit trail behind each.
- Revocation and its published 60-second bound, including abuse blocking and the interstitial.
- Branded domain verification, certificate provisioning and renewal alerting.
- Click collection, bot classification, rollups, the export stream and the billing meter feed.
- Destination screening at creation and periodically thereafter, the review queue and the appeal path.
Explicitly out of scope
- Campaign management, email or SMS sending, and CRM integration. The platform publishes click data and stops there.
- The destination page. The platform never fetches, renders, proxies or rewrites it — it names a location.
- Link-in-bio pages, QR design and creative tooling. QR generation appears only as a Phase 3 item over the same code.
- Customer-side attribution modelling. The platform reports clicks, not conversions.
- Consent management, because the click record deliberately holds no identifier that would require it.
What a four-week prototype should prove
Four of this architecture's claims are cheap to test and expensive to be wrong about. A prototype that proves these can be built on; one that skips them is a demonstration of HTTP redirection, which is the easy half.
- The revocation promise end to end: kill a link and measure, from outside, when the last PoP stops serving it — across all regions, with the edge cache warm, repeated a thousand times and reported at p99 rather than as an average.
- Uniqueness under a real race: thousands of concurrent claims for the same alias across regions, asserting exactly one winner and a named conflict for everyone else, including claims against retired codes.
- Hot key absorption: one code taken from zero to 80,000 requests per second in thirty seconds with a cold cache, measuring both its own latency and the p99 of every other code in the same region throughout.
- Counting from the edge log at the full cache hit rate: 97% of clicks never reaching application code, reconciled against the edge counters to within ±0.5%, because this is the claim that makes ADR-08 and ADR-10 compatible.
- Fail the write region mid-import and confirm creation is refused with a retryable error and the bulk job resumes without issuing a second code for any row.
- Flush every regional hot set and deny-set at once and confirm the deny-set is restored first, so a killed link never resolves during the rebuild window.
- Flip a reputation feed's verdict on a link with live traffic and confirm it stops resolving within 60 seconds at every PoP, with the evidence recorded and the owner able to see the reason.
- Let a link's TTL expire while its 302 is cached at a PoP and confirm the clicker gets a 410 rather than a redirect within the published bound, and that the expiry is not discoverable early by an enumerating client.
Open risks, carried rather than hidden
| Risk | If it lands | Response |
|---|---|---|
| The edge cache hit rate does not reach 97% | Cost per million redirects rises towards the origin-read price, the resolver fleet must grow several times over, and the 40 ms p99 becomes a cold-path p99 instead. | Measure the real code popularity distribution early. The designed levers are a longer TTL (which weakens the revocation bound and must be traded explicitly), a larger hot set, and pre-warming on creation. |
| Revocation is promised at 60 seconds and delivered at five minutes | The platform's safety claim fails silently: dashboards stay green because the mechanism is healthy while the end-to-end result misses the bound. | The synthetic revocation probe in view 20 is the control, alarmed on the promise rather than on component health. The 30 s edge TTL is a design parameter and any proposal to raise it must state the new bound. |
| A single write region constrains creation availability | Regional failover makes link creation unavailable for up to five minutes, which a large customer's bulk import will eventually hit. | Accepted deliberately: refusing creation is preferable to two winners for one alias. Bulk imports are jobs with resumable semantics, so a failover delays rather than fails them. |
| Bot classification is wrong in the direction customers notice | Either campaigns are judged on preview traffic, or genuine clicks are discarded as bots — and the second is far worse because it is invisible. | Classification tags and never blocks; the raw class is retained so a reclassification can be replayed over history rather than requiring the data to be re-collected. |
| The platform becomes phishing infrastructure | The shared domain's reputation is destroyed by browser and mail-provider blocklists, which takes every legitimate tenant's links down with it. | Screening before issuance, periodic re-screening, challenges on bulk creation from unverified accounts, and a revocation path fast enough that response time is measured in seconds. |
The reasoning behind every component and technology choice is in the Architecture Decision Record: 16 records across 5 areas, each with the alternatives that lost and what the choice costs.