Study Notes
Notecore

Feeds, Notifications & CMS

Three services that all answer one question — "what should this user see, and when?" A recommendation feed the user pulls , notifications the backend pushes , and a content management system whose article-author-tag-category web finally makes graph databases the obvious choice. All three are wired together by the same change-data-capture-into-Kafka backbone.

35 min read·verified Content review 2026·reviewed 2026-08-30

After this you should be able to

  • Explain the core idea in Feeds, Notifications & CMS
  • Apply Feeds, Notifications & CMS in a representative design
Distributed Systems · Chapter 06

Three services that all answer one question — "what should this user see, and when?" A recommendation feed the user pulls, notifications the backend pushes, and a content management system whose article-author-tag-category web finally makes graph databases the obvious choice. All three are wired together by the same change-data-capture-into-Kafka backbone.

🧭
How to read this

This page merges three small course sections that share a spine. Feed and Notifications are near-twins — both react to a new row in VideoMetadata, both fan it out to subscribers — the difference is pull vs. push. CMS is a standalone argument that builds, step by step, to why a graph database beats both relational and document stores for deeply connected content. Anything marked [+] is context I added; everything else is rewritten from the seven teaching lessons.

Part A · Video / News Feed Service

01

The feed starts with Change Data Capture#

The VOD home page pulls its feed from an API. That feed has two sources: (1) videos from channels the user follows, and (2) videos matched to the user's watch behavior (plus trending/popular). Both compute their results into a feed cache; the API reads from that cache on request.

Start with source (1). When a channel uploads a video, a row lands in VideoMetadata — but how does the system know a row was added? That's Change Data Capture (CDC): monitoring a data source for changes and streaming them in real time to another service. The lesson weighs four ways to do it:

CDC approachVerdict here
Timestamp column (LastUpdated ), polled periodically Doesn't fit — we add new rows, not update existing ones; and frequent polling loads the table.
Table snapshots, compared over time Storage-intensive; won't scale with heavy writes.
Read the DB transaction log Viable — changes are read from the log.
Trigger an event on change → push to Kafka [+] chosen The approach used: a write triggers an event that moves the change to a Kafka queue asynchronously.
🔑
Finding a channel's subscribers, fast

Once the video streams into Kafka, take its ChannelId and find the channel's subscribers. Instead of querying the ChannelSubscription bridge table every time, cache it in a key-value store (Redis): key = ChannelId, value = list of subscriber UserIds. A module called the Channel Subscription Feed polls the Kafka queue and pushes the video into each subscriber's recommendation feed.

02

The recommendation object, in a key-value store#

Store each user's feed in a key-value store for O(1) access: key = UserId, value = a list of VideoRecommendation objects.

class VideoRecommendation {
  int id;
  List<Integer> videoIds;
  boolean polledByUser = false;
  // ...other fields
}

When the Channel Subscription Feed pulls, say, 25 videos to recommend to a user, it adds them to videoIds, sets polledByUser = false, wraps it in a VideoRecommendation object, and appends it to that user's list. When the user requests recommendations, one or more objects are returned, polledByUser flips to true, and once sent they can be discarded from the cache to free space.

The second source: watch behavior

A Recommendation Engine computes videos from viewing behavior (trending/popular included). It ingests VideoMetadata via the same CDC-into-Kafka flow for new videos. To surface trending content it must re-query the table for updated views/likes/comments — so keep a copy of VideoMetadata the engine queries continually, sparing the origin table load. The engine writes its results into the same VideoRecommendation list. (The actual matching is machine learning, out of scope.)

VideoMetadata new row CDC Kafka queue Channel Sub Feed + Redis chan→subs Recommendation Engine VideoMeta copy feed cache key: UserId → [VideoRecommendation] API home page pull
Figure 1 — The feed pipeline: CDC → Kafka → two consumers → a per-user feed cache the home-page API pulls from. Original diagram after Figures 6.2–7.0.

[+] Swap "video" for "image" and this is exactly how a Pinterest or Instagram feed works too.

Part B · Push Notifications Service

03

Notifications: the same flow, reversed#

The notification workflow mirrors the feed's: a new VideoMetadata row triggers an event → Kafka queue → a Notification Service polls it, takes the ChannelId, looks up subscribers in the key-value store, and generates a notification object per subscriber:

class Notification {
  int id;
  String notificationMessage;
  Integer videoId;
  String videoURL;
  // ...other fields
}

These land in each user's notification feed in a key-value store (key = UserId, value = list of Notification objects). The crucial difference from the feed: notifications are pushed, not pulled.

📡
Push over a persistent connection

The backend pushes notification objects to the client over a persistent connection (an HTTP push technology). Compare YouTube: refreshing the home page pulls the recommendation feed, but new-video notifications are pushed. Notifications must arrive in real time as the event happens — you can't wait for the user to ask. What if they never send a request? Or request a live-event notification after the event ended? That defeats the purpose.

Feed = PULL feed cache client requests → ← data Notifications = PUSH backend client persistent conn.
Figure 2 — Same underlying pipeline, opposite delivery: the feed is pulled on request; notifications are pushed over a persistent connection. Original diagram after Figure 7.1.
04

Two problems at ESPN/YouTube scale#

Problem 1: resource cost A channel with 50M subscribers uploading one video means 50M real-time notifications — for one channel. YouTube has ~110M channels (~50M active). A server can hold only so many open connections.
Problem 2: user experience Follow 100+ active channels averaging 5 videos/week each = 500 notifications/user/week (higher with shorts). That overwhelms people — bad UX.
🔔
The fix: prioritize notifications

Don't bombard viewers with every event — send a curated few catered to their browsing/watch behavior, plus the important ones. This is exactly why YouTube introduced the bell icon: you only get notifications for channels where you pressed the bell. If a system has too many notifications to handle, introduce a similar opt-in, or prioritize by watch behavior with machine learning.

Part C · Content Management Service (CMS)

05

Picking a data model: knowledge graphs#

On a sports site, content is videos (already covered) plus articles with images. The CMS has two sub-services: a Content Storage Service (takes content from writers, persists it) and a Content Delivery Service (serves it to readers). An article has a title, summary, content, images, categories, tags, and one or more authors — with one-to-many relationships to categories (sport, tournament), tags (club, country, game type), and authors.

Which database? The lesson eliminates two candidates:

CandidateWhy it struggles
Document DB (denormalized) The main queries are "articles by author X / category Y / tag Z." To answer fast you'd need a separate collection per category, tag, and author — with massive data overlap (a baseball article duplicated across all its categories' and tags' collections). Undesirable.
Relational A "better choice" than documents here — but deep, connected queries demand many joins, and latency climbs with relationship depth.
Graph (knowledge graph) [+] chosen Store articles, categories, tags, authors as nodes with edges as relationships. Purpose-built for connected data.
🕸️
Knowledge graphs in the wild

They power CMSs like Wikipedia and Bloomberg with big, complex data. A Wiki article on a historical war links continents, countries, battles, locations, commanders, allies, hostile forces — letting a reader wander from one battle to related events, people, and nations. That deep interlinking drives engagement and retention. Google and Facebook maintain knowledge graphs to surface relevant data from petabytes at low latency. Modeling all of history's wars in a document or relational DB would be a nightmare — denormalization exhausts disks and joins send latency "soaring like a NASA rocket."

06

Modeling the content graph#

A graph model has nodes, node properties, directed relationships, and labels. Authors (Ron, Jim, Nancy…), categories (Baseball, Football, Cricket, Boxing), tags (India, Ashes, Yankees, Cubs…), and articles all become nodes with directed edges between them. In Neo4j, nodes are created/queried with the Cypher query language (Cypher is to Neo4j what SQL is to a relational DB).

Author Ron Article #412 Article #417 Category Tag Yankees Tag Cubs AUTHORED AUTHORED IN CATEGORY TAGGED TAGGED
Figure 3 — The content graph: query by traversing edges from a node (AUTHORED, IN CATEGORY, TAGGED) — no joins. Original diagram after Figure 7.3.

Two query types

  • Query 1 — "all articles by author Ron / in category Baseball / with tag Yankees." Locate the node and traverse its edges (AUTHORED, HAS AN ARTICLE, etc.).
  • Query 2 — "articles by Ron and in Baseball and tagged Yankees." Locate Ron, traverse to his articles, then scan those articles' relationships to the category and tag nodes.
⚡
The payoff: no joins

Query 2 on a relational DB needs multiple joins — and the deeper the scan, the more joins. The graph needs none, so responses are dramatically quicker. That's the whole reason graphs win for connected data.

Tuning the graph

  • Redundant properties — if Query 2 is very frequent, add categories/tags as properties on article nodes (as well as separate nodes), shrinking scan depth. Writes update multiple places, fine since tags rarely change.
  • Relationship properties — edges carry metadata too: an Author —WRITES ABOUT→ Baseball edge can note long-form / summaries / tweets.
  • Labels — group nodes by role (e.g. label all clubs playing football in Asia) to speed frequent queries.
  • Node properties — authors carry id/name/designation/experience; articles carry id/length/content/image URLs. In Neo4j properties are stored key-value (node id → properties). If property data is large/complex, keep it in a document or relational DB and store a pointer in the node — a polyglot model.
🎯
Graphs are the basis of recommendations

Connected data links nodes that sit far apart. If Shreya likes author Ron, and Ron reads authors X and Y, the graph can recommend X and Y's articles to Shreya. Pulling that off relationally in near-real-time would need an abysmal number of joins — this is where graphs shine.

07

CMS core architecture#

Authors write in a UI and hit publish. The article goes to an Article Processor, which:

  1. Triggers a serverless event that, via a Kafka queue, sends the images to an Image Processing Service — the queue loosely couples the two so image processing works at its own pace.
  2. Persists the article to the graph database.
  3. Meanwhile the Image Processing Service compresses/enhances images and stores them in a cloud object bucket.
  4. On success, another serverless event updates the image URLs as properties on the article node in the graph DB.
  5. Once all events succeed, the Article Processor returns a publish-success response, and published content is cached per business logic.
🔄
Async or sync — the result is the same

You could also do it synchronously: optimize images, store them, update the article object with URLs, then persist everything to the graph DB. It doesn't matter — in both cases the Article Processor won't send success until the node's properties carry the image URLs.

Content delivery reuses the VOD pattern exactly: reader → edge cache (hit → served) → regional cache → content-serve service at the origin. No need to reinvent it.

08

Recap#

Feed via CDC A new VideoMetadata row triggers an event → Kafka. Chosen over timestamp polling / snapshots / log reads. Redis maps channel → subscribers.
Recommendation object Key-value store: UserId → list of VideoRecommendation. polledByUser flips on send, then discarded. A Recommendation Engine adds behavior-based picks.
Notifications = push Same CDC→Kafka pipeline, but a Notification Service pushes objects over a persistent connection — real-time, not pull.
Prioritize at scale 50M-subscriber channels + 500 notifications/user/week → curate. YouTube's bell icon is opt-in prioritization; ML can rank the rest.
CMS → graph Articles/authors/categories/tags are deeply connected. Documents duplicate; relational over-joins; a knowledge graph fits (Wikipedia, Bloomberg).
Graph tuning + arch Redundant properties, edge metadata, labels, polyglot pointers. Article Processor + Image Service via Kafka; delivery reuses the VOD edge stack.
💡
The single idea underneath it all

These three services are one idea seen from three angles: a write in one place has to become the right experience for millions of specific people. Change Data Capture turns a database write into a stream; Kafka fans that stream out without coupling anyone; and then the shape of the destination decides everything. A feed the user checks → cache it and let them pull. A moment they can't miss → push it, but curate so you don't drown them. Content whose value is its connections → store it as a graph so "related" is a traversal, not a pile of joins. Match the delivery to how the human actually consumes it.

09

Further reading#

Both external references resolve live. The author's own course cross-links (message queues, persistent connections, document/graph DBs, polyglot persistence) are omitted.

[1]
Cypher Manual — Neo4j Why read it: the query language used to create and traverse the content graph in Sections 06–07.
[2]
Graph traversal — Wikipedia Why read it: DFS/BFS, the algorithms behind how graph queries walk the content graph (Section 06).

Rewritten in full from three sections of Designing Modern Web-Scale Distributed Services — the Video/News Feed Service, Push Notifications Service, and CMS chapters — merged here because they share the same CDC-into-Kafka backbone. Combines seven teaching lessons (Recommendation Feed Parts 1–2; Notifying Viewers; Picking the Data Model & Knowledge Graphs; Content Graph Parts 1–2; CMS Core Architecture). All three diagrams are original SVGs built from the lessons' descriptions of their figures (6.2–7.4), not reproductions of source images. [+] marks context added beyond the source. External links were verified live on Aug 13, 2026; the author's own course cross-links were omitted.