I wanted one place to write and manage my technical articles, while still showing selected posts across three separate websites. The obvious solution was to expose the blog through an API and let every frontend fetch the same content. That works, but by itself it creates new questions around repeated API requests, stale data, synchronization, duplicate content, and SEO.
The architecture I ended up with is simple once each piece has a clear job: one central CMS owns the articles, one API serves selected posts, the portfolio sites cache that data for speed, webhooks tell them exactly when the cache is outdated, and canonical URLs identify blog.sanchit.pro as the original publication.
This article walks through that architecture from the beginning, including what would happen with the simpler alternatives, why I did not choose them, and what terms such as caching, cache invalidation, webhooks, canonical URLs, and revalidation actually mean.
Imagine having three websites:
blog.sanchit.prosanchit.prosanchitpandey.com.np
Now imagine wanting a blog on all three.
The obvious solution sounds ridiculously simple:
Create the article once, expose an API, call that API from all three websites, and render the article everywhere.
Technically, that works.
And initially, that was almost exactly how I thought about the problem.
But once I started looking at what would actually happen in production, several questions appeared.
Where should the article really be stored?
Should every website have its own database?
Should every visitor trigger another API request?
What happens if the blog API temporarily goes down?
How does one portfolio know that I just published something new?
And perhaps the biggest question:
If the exact same article exists on three different domains, what does Google do with them?
That simple "just call an API" idea had quietly turned into an architecture problem.
This is how I solved it.
The Goal: Write Once, Publish Everywhere I Choose
I didn't want three blogging systems.
I definitely didn't want this workflow:
Write article
↓
Publish on blog.sanchit.pro
↓
Copy article
↓
Publish on sanchit.pro
↓
Copy article again
↓
Publish on sanchitpandey.com.np
That would mean three places to update whenever I fixed a typo, changed an image, updated some code, or corrected outdated information.
Sooner or later, the three copies would drift apart.
One might contain the latest code.
Another might have an older title.
One might have a broken image.
And I'd have three separate places to maintain SEO metadata.
No thanks.
What I wanted was this:
Write once
↓
Publish once
↓
Choose whether it should appear on my portfolios
↓
Everything else happens automatically
The central blog would always contain every published article.
The portfolio sites would contain only the articles I selected.
So the rule became:
showOnPortfolios = false
blog.sanchit.pro ✓
sanchit.pro ✗
sanchitpandey.com.np ✗
or:
showOnPortfolios = true
blog.sanchit.pro ✓
sanchit.pro ✓
sanchitpandey.com.np ✓
Importantly, I didn't want separate switches for my two portfolio sites.
They act as one distribution group.
Step 1: Establishing a Single Source of Truth
The first decision was probably the most important:
Only one system should own the article.
In my case, that's:
blog.sanchit.pro
It contains the dashboard, article editor, publishing system, database, images, metadata, categories, tags, SEO fields, and distribution settings.
Conceptually:
blog.sanchit.pro
────────────────
Dashboard
│
↓
Blog Backend
│
↓
Database
The portfolio websites do not get their own blog databases.
They do not get their own editors.
They do not maintain another copy of each post.
That idea is commonly called a single source of truth.
The term sounds more complicated than it is.
It simply means:
If two systems disagree, which system contains the real answer?
For my articles, the answer is always:
blog.sanchit.pro.
Suppose I change this sentence:
Redis is required for this architecture.
to:
Redis is optional for this architecture.
I update it once in my central CMS.
I don't have to remember where else I copied the article.
That is the practical value of having one source of truth.
Step 2: Letting the Other Websites Read the Articles
Now there was another problem.
My two portfolio websites are completely separate frontends.
They can't magically read the database belonging to blog.sanchit.pro.
So they need a controlled way to ask for the content.
That's what the API does.
Conceptually, my blog exposes something like:
GET /api/v1/portfolio/posts
The response might look roughly like:
[
{
"title": "Building Secure Storage in React Native",
"slug": "secure-storage-react-native",
"excerpt": "How I approached secure local storage...",
"featuredImage": "...",
"publishedAt": "...",
"canonicalUrl": "https://blog.sanchit.pro/secure-storage-react-native"
}
]
That endpoint only returns articles where:
published = true
AND
showOnPortfolios = true
So the architecture now looks like:
DATABASE
│
↓
blog.sanchit.pro
│
Portfolio API
│
┌──────────┴──────────┐
↓ ↓
sanchit.pro sanchitpandey.com.np
One backend.
One article database.
One API.
Multiple consumers.
Wait!! Why Not Just Call the API Every Time?
This is where things get interesting.
The simplest portfolio implementation could be:
Someone visits sanchit.pro/blog
↓
sanchit.pro calls blog.sanchit.pro API
↓
Blog server queries database
↓
API sends articles
↓
Portfolio renders page
Nothing is technically wrong with that.
For a small site, it could work perfectly well.
But imagine 10,000 people visiting my portfolio.
If every request triggers another request to my blog backend, the flow becomes:
Visitor 1 ──→ Portfolio ──→ Blog API
Visitor 2 ──→ Portfolio ──→ Blog API
Visitor 3 ──→ Portfolio ──→ Blog API
Visitor 4 ──→ Portfolio ──→ Blog API
...
Visitor 10,000 ────────────→ Blog API
Most of those visitors are asking for exactly the same article data.
And that data probably hasn't changed since I last published something.
That's a lot of repeated work for information that barely changes.
Step 3: Caching the Blog Data
This is where caching enters the architecture.
"Cache" is one of those words beginners encounter everywhere without anyone explaining what it actually means.
So here's the simplest possible explanation.
Imagine you run a restaurant.
Every customer asks:
What's today's menu?
You could walk into the kitchen and ask the chef every single time.
Customer 1:
What's the menu?
You ask the chef.
Customer 2 arrives five seconds later:
What's the menu?
You ask the chef again.
Customer 3:
Same thing.
That works.
But it's ridiculous because the menu hasn't changed.
Instead, you write today's menu on a board.
Now customers can simply read the board.
The chef only needs to update the board when the menu changes.
That board is basically a cache.
In My Architecture
Instead of doing this for every visitor:
Visitor
↓
Portfolio
↓
Blog API
↓
Database
the portfolio can do this:
Visitor
↓
Portfolio
↓
Previously cached blog data
↓
Page
Much faster.
The central API doesn't need to answer the same question thousands of times.
Depending on the framework and hosting platform, that "cache" might be:
a framework data cache,
a pre-rendered page,
ISR-generated HTML,
a server cache,
a CDN cache,
or several of those working together.
The concept remains the same.
Reuse something that hasn't changed instead of rebuilding it unnecessarily.
But Caching Creates a New Problem
Suppose my portfolio cached this:
Article A
Article B
Article C
Then I publish:
Article D
The database now contains:
Article A
Article B
Article C
Article D
But my portfolio may still have the old cached result:
Article A
Article B
Article C
That's the downside of caching.
Caching makes things fast by reusing an older result.
But when the original data changes, something has to tell the cache:
Hey, what you're holding is outdated now.
And that is exactly why I use a webhook.
Step 4: The Webhook
A webhook is another term that sounds more complicated than it really is.
Think of the difference between these two situations.
Without a webhook
You are waiting for a package.
Every five minutes, you open the door:
Has my package arrived?
Five minutes later:
Has it arrived now?
Again:
What about now?
You're repeatedly checking even though nothing has happened.
That's essentially polling.
With a webhook
Instead, the delivery person rings your doorbell when the package arrives.
You don't keep checking.
Something happens, and you get notified.
That's essentially a webhook.
What My Webhook Actually Does
When I publish a new article:
blog.sanchit.pro
↓
Article saved
↓
Webhook sent
↓
"Hey portfolios, the blog changed."
Both portfolio websites receive that message:
blog.sanchit.pro
│
Blog changed
│
Send webhook
↙ ↘
↓ ↓
sanchit.pro sanchitpandey.com.np
The webhook does not contain my entire blogging system.
It doesn't replace the API.
The API and webhook have completely different jobs.
API
The API answers:
Give me the article data.
Webhook
The webhook says:
Something changed. Your cached version may now be outdated.
That distinction made the entire architecture much easier for me to reason about.
What Happens After the Webhook?
Suppose this is currently cached:
Article A
Article B
Article C
I publish:
Article D
The sequence becomes:
1. Article D saved to central database
2. blog.sanchit.pro sends webhook
3. Portfolio receives webhook
4. Portfolio marks old cached blog data as outdated
5. Portfolio fetches fresh data from central API
6. Fresh data becomes:
Article A
Article B
Article C
Article D
7. New result is cached
8. Visitors receive the fresh page
You'll often hear step 4 called:
cache invalidation
Which sounds terrifying until you translate it into normal English.
It simply means:
Stop trusting this cached version because something changed.
That's it.
Nothing mystical happening behind the curtain.
Why Not Just Refresh the Cache Every Minute?
That's another perfectly valid architecture.
For example:
Every 60 seconds
↓
Check Blog API
↓
Update cache
But think about what happens when I don't publish anything for three days.
The portfolio keeps checking:
Anything new?
No.
Anything new?
No.
Anything new?
No.
A webhook is cleaner because the central system already knows exactly when something changes.
So instead of asking:
Did something happen?
over and over again, the blog simply says:
Something happened.
API and Webhook Work Together
This is probably the most important distinction in the whole article.
They are not competing technologies.
They work together.
API
=
"Give me data."
Webhook
=
"The data changed."
Together:
blog.sanchit.pro
│
Central Database
│
┌──────┴──────┐
│ │
API Webhooks
│ │
Gives data Announces change
│ │
└──────┬──────┘
↓
Portfolios
Step 5: Rendering the Article Properly
Another important decision was where rendering happens.
The easiest frontend implementation would be something like:
useEffect(() => {
fetch("https://blog.sanchit.pro/api/v1/portfolio/posts");
}, []);
That means:
browser downloads the portfolio page,
JavaScript loads,
JavaScript calls the API,
API returns content,
browser finally displays the articles.
This works.
But I didn't want the core article experience to depend entirely on that process.
Instead, the portfolio can use server-side rendering, static generation, ISR, server components, or the equivalent mechanism supported by its framework.
The goal is simple:
When someone requests:
sanchit.pro/blog/my-article
the returned page should already contain meaningful article HTML.
Conceptually:
Request
↓
Portfolio server
↓
Cached/pre-rendered article
↓
Complete HTML
↓
Browser
Google can render JavaScript, but for SEO-critical content I still prefer meaningful server-rendered HTML. Google's own guidance explains how JavaScript rendering affects crawling and indexing, and why crawlable links matter for discovery. See Google Search Central's JavaScript SEO guidance and link best practices.
For me, this wasn't about trying to "trick Google."
It was simply good architecture:
faster initial content,
fewer loading states,
better resilience,
easier crawling,
better user experience.
Then Came the SEO Problem
At this point I had solved:
centralized content,
distribution,
API access,
caching,
synchronization,
rendering.
But there was still a potentially serious design question.
If I publish one article on:
blog.sanchit.pro/my-article
and display exactly the same article on:
sanchit.pro/blog/my-article
and:
sanchitpandey.com.np/blog/my-article
I now have three URLs containing substantially the same content.
My first instinct could have been:
Great! Three websites, three chances to rank!
Unfortunately, search doesn't quite work that way.
Google performs a process called canonicalization for duplicate or very similar pages. It chooses a representative URL, called the canonical, from the duplicate set. Google documents this process in its canonicalization guide.
In other words, creating three identical copies does not simply multiply SEO value by three.
Google may decide:
These are basically the same document. Which URL should represent it?
What Is a Canonical URL?
Again, let's remove the SEO jargon.
Suppose I have the exact same photograph saved as:
photo-final.jpg
photo-final-copy.jpg
photo-really-final.jpg
They are three files.
But conceptually, they represent one image.
Canonicalization is somewhat similar.
I explicitly tell search engines:
Yes, these portfolio pages contain this article, but this URL is the original/preferred version.
So the original article has:
https://blog.sanchit.pro/my-article
as its canonical.
The portfolio copy contains something conceptually like:
<link
rel="canonical"
href="https://blog.sanchit.pro/my-article"
/>
The other portfolio does the same.
So my SEO architecture becomes:
blog.sanchit.pro/article
↑
│
Preferred version
│
┌──────────┴──────────┐
│ │
sanchit.pro/blog/article sanchitpandey.com.np/blog/article
│ │
└──── canonical ──────┘
Google recommends rel="canonical" as one of the signals website owners can use to indicate a preferred URL among duplicate or very similar pages. See Google's guide to consolidating duplicate URLs.
Does Google "Penalize" My Portfolio Because the Article Is Duplicated?
This needs an important clarification.
Duplicate content does not automatically mean some mysterious SEO penalty.
The more common issue is deduplication.
Google recognizes that several URLs represent the same or highly similar content and chooses a representative version for search results.
So the problem isn't necessarily:
Google hates my website now.
It's more like:
Google doesn't need to show three copies of essentially the same thing.
That is precisely why having a deliberate canonical strategy matters.
Why Keep the Article on the Portfolios at All?
This is a fair question.
If I want the central blog to rank as the original, why display the article elsewhere?
Because the portfolio copies still have value for people.
Imagine someone discovering my portfolio because they saw one of my projects.
They can move naturally from:
My Project
↓
Related Engineering Article
without being thrown into a completely separate experience immediately.
My portfolio can have:
Projects
Experience
About
Writing
as one coherent personal website.
The central blog still owns the original publication, but the portfolio gains a much richer content experience.
My Sitemap Strategy
Sitemaps are another part of this system.
A sitemap is essentially a machine-readable list telling search engines:
These are important URLs on my site that you may want to crawl.
Google describes sitemaps as files that provide information about important pages and files, including information such as when a page was last updated. See Google Search Central's sitemap documentation.
The important detail in my architecture is that I don't blindly treat every duplicate portfolio article as another independent canonical document.
My primary article lives in the sitemap for:
blog.sanchit.pro
Meanwhile, the portfolio sitemaps primarily describe original portfolio content such as:
/
/about
/projects
/projects/project-name
/blog
The exact sitemap choices need to remain consistent with the canonical strategy.
Internal Linking Also Matters
Publishing a URL isn't enough.
Search crawlers still need paths through the website to discover and understand pages.
Google explains that links help it discover pages and understand relevance. Its link best practices also recommend crawlable links with descriptive anchor text.
So when I publish a selected article, it can immediately appear in places like:
Homepage
↓
Latest Writing
and:
/blog
↓
Article
and possibly:
Project
↓
Related Engineering Article
This also makes the website dramatically more useful for humans.
If you want to explore the rest of my technical writing, you can browse the latest articles on this blog. Keeping useful pages connected through normal links also gives readers, and crawlers, a clear path through the site.
Step 6: Fast Search Discovery
Once an article is published, I want everything I control to happen immediately.
My publishing flow can look approximately like this:
PRESS PUBLISH
│
↓
Save article to DB
│
↓
Article available
│
┌───────────────┼────────────────┐
↓ ↓ ↓
Update Sitemap Update Feed Clear Caches
│ │
↓ ↓
Search discovery Send Webhooks
│
┌───────────┴───────────┐
↓ ↓
sanchit.pro sanchitpandey.com.np
│ │
Revalidate Revalidate
│ │
└───────────┬───────────┘
↓
Fetch fresh API
I can also notify participating search engines through IndexNow when content has been added, updated, or deleted. That is the exact use case described in the official IndexNow documentation.
That does not guarantee ranking.
It simply removes unnecessary delay in telling participating search systems:
This URL changed.
What Happens During an Actual Publish?
Let's walk through one real example.
Suppose I create:
How I Built Secure Local Encryption in React Native
Inside my central CMS I select:
Show on portfolios: ✓
1. The article enters the central database
The authoritative version now exists inside:
blog.sanchit.pro
2. The public blog page becomes available
For example:
blog.sanchit.pro/secure-local-encryption-react-native
3. SEO metadata is generated
That includes things like:
Title
Meta description
Canonical
Open Graph data
Article structured data
Author information
Published date
Updated date
4. The sitemap updates
The new canonical URL becomes discoverable through the blog sitemap.
5. Internal links update
The article can appear under:
Latest Posts
Category
Related Writing
Author page
6. Search notification happens where supported
For example, IndexNow can be notified.
7. Both portfolio webhooks fire
The central system tells:
sanchit.pro
and
sanchitpandey.com.np
that something changed.
8. Their old caches become invalid
Remember:
"Invalidate" just means:
Don't keep serving the old cached result.
9. They fetch the API again
Now the central API includes the new post.
10. They generate fresh pages
Visitors get the new article without me touching either portfolio.
That is the whole system.
What Happens When I Edit an Article?
Almost exactly the same thing.
Suppose I correct an outdated code example.
I don't log into three websites.
I update:
blog.sanchit.pro
Then:
Save update
↓
Central database changes
↓
Sitemap/metadata updated where necessary
↓
Portfolio webhook fired
↓
Portfolio caches invalidated
↓
Fresh version retrieved
One edit.
Three frontends updated.
What Happens When I Remove an Article From My Portfolios?
Let's say an article is useful on my blog but no longer something I want featured through my professional portfolio.
I change:
showOnPortfolios = true
to:
showOnPortfolios = false
I do not delete the article.
It stays available on:
blog.sanchit.pro
But the portfolio API stops including it.
The central system sends the distribution-change webhook.
Both portfolios invalidate the relevant cached pages.
The article disappears from both portfolios.
Again:
One setting.
No manual synchronization.
Why I Didn't Give Each Portfolio Its Own Database
I could have done this:
Central DB
↓
copy
↓
sanchit.pro DB
Central DB
↓
copy
↓
sanchitpandey.com.np DB
But now I have synchronization problems.
What happens if copying succeeds for one portfolio but fails for another?
What happens when an article changes?
What happens when its slug changes?
What if an article is deleted?
What happens to images?
What happens to tags?
Now I've effectively built distributed database synchronization for... a personal blog.
That is significantly more complexity than I need.
So the portfolio sites remain consumers, not owners.
Why I Didn't Make Three Separate CMSs
This approach:
CMS 1 → blog.sanchit.pro
CMS 2 → sanchit.pro
CMS 3 → sanchitpandey.com.np
would technically give maximum independence.
It would also give me maximum maintenance.
Every change becomes three changes.
Every bug becomes three potential bugs.
Every schema update becomes three updates.
Every SEO adjustment becomes three updates.
Every article correction becomes three edits.
That's the opposite of what I wanted.
Why I Didn't Just Use the API Without Caching
I could.
And for a very small application, I might.
It would have the advantage of simplicity:
Request
→ API
→ latest data
No stale cache.
No webhook.
No invalidation logic.
The tradeoff is that freshness is achieved by asking the origin repeatedly.
My architecture prefers:
Request
→ fast cached/pre-rendered data
and:
Content changes
→ webhook
→ refresh only when necessary
That's a slightly more sophisticated system, but one that scales much more cleanly.
The Tradeoffs
No architecture is free.
This one has several advantages.
Advantages
One place to write
I never manually copy an article between domains.
One authoritative database
There is no question about which version is correct.
Fast portfolio pages
Caching avoids unnecessary central API requests.
Automatic updates
Webhooks remove most synchronization delay.
Independent frontends
Each portfolio can still have its own design and technology.
Controlled publishing
Not every blog article has to appear on the portfolios.
Cleaner SEO strategy
Canonical URLs clearly identify the original article.
Easier maintenance
Changing the blog backend does not mean managing three separate content systems.
Disadvantages
More architecture than a simple API call
I now have caching and webhooks to maintain.
Webhook security matters
I shouldn't let arbitrary internet users call my cache-revalidation endpoint.
The webhook therefore needs authentication/signature validation.
Cache invalidation needs testing
If invalidation fails, the portfolio may temporarily show an older version.
The central blog is important infrastructure
If blog.sanchit.pro disappears permanently, the portfolios lose their authoritative content source.
Caching provides some resilience, but good monitoring and backups still matter.
Canonicalization means I am deliberately choosing one primary ranking URL
I am not pretending three duplicate pages are three separate pieces of content.
That is intentional.
Securing the Webhook
A webhook endpoint shouldn't simply trust requests saying:
Hey, clear your cache!
Otherwise anyone who discovers the endpoint could repeatedly trigger expensive rebuilds.
So the blog signs the webhook using a secret shared between the servers.
Conceptually:
BLOG SERVER
payload
+
secret
↓
signature
Then the portfolio receives:
payload
+
signature
and checks:
Does this signature match what I expect?
If yes:
Process webhook
If no:
Reject request
The secret never needs to enter the browser.
It stays between servers.
What If the Webhook Fails?
This is another important engineering decision.
Publishing my blog should not fail because one portfolio is temporarily unavailable.
Bad architecture would be:
Publish article
↓
Portfolio webhook fails
↓
ENTIRE PUBLISH FAILS
My preferred behavior is:
Publish article
↓
Article successfully stored
↓
Webhook attempted
↓
Portfolio unavailable
↓
Log failure / retry
The central blog is the source of truth.
Portfolio distribution is secondary.
That separation makes the system more resilient.
Does This Need Redis?
No.
At least, not automatically.
This is another thing I wish beginners were told more often:
"Caching" does not automatically mean "install Redis."
Modern frameworks and deployment platforms can provide:
request caching,
data caching,
static generation,
page caching,
CDN caching,
revalidation.
Redis becomes useful when your architecture genuinely requires a shared external cache or other Redis capabilities.
Adding technology simply because it sounds scalable usually makes a project harder, not better.
How This Differs From Headless WordPress
Interestingly, this architecture is conceptually similar to how people use WordPress as a headless CMS.
WordPress can expose posts through its REST API.
A separate frontend can consume those posts.
For example:
WordPress
↓
REST API
↓
Next.js frontend
That doesn't mean the frontend must call WordPress on every visitor request.
The frontend can still:
cache responses,
pre-render pages,
regenerate periodically,
or use a webhook/event mechanism to refresh content after publication.
So APIs and webhooks are not competing approaches there either.
The same principle applies.
My Final Architecture
After all those decisions, the system looks roughly like this:
┌─────────────────────┐
│ blog.sanchit.pro │
│ │
│ Dashboard + CMS │
└──────────┬──────────┘
│
↓
┌─────────────────────┐
│ Central Database │
│ │
│ All Articles │
└──────────┬──────────┘
│
┌────────────────┴────────────────┐
│ │
↓ ↓
Public Blog Portfolio API
│
showOnPortfolios=true
│
┌──────────────────┴──────────────────┐
│ │
↓ ↓
┌─────────────────┐ ┌─────────────────┐
│ sanchit.pro │ │sanchitpandey.com│
│ │ │ .np │
│ Cached /blog │ │ Cached /blog │
└────────┬────────┘ └────────┬────────┘
↑ ↑
│ │
└──────────── Webhooks ───────────────┘
↑
│
Article changed
And the data flow is:
CMS
↓
Database
↓
API
↓
Portfolios
while the change-notification flow is:
CMS
↓
Something changed
↓
Webhook
↓
Portfolio cache refresh
Those are two separate flows.
Understanding that distinction was probably the biggest conceptual breakthrough in the whole architecture.
The Architecture in One Sentence
If I had to explain the entire system to someone in one sentence:
I store every article once, expose selected articles through one central API, cache them on my portfolio frontends for speed, use webhooks to refresh those caches only when something changes, and use canonical URLs so search engines understand which copy is the original.
That's really it.
Everything else is implementation detail.
What I Learned Building This
The interesting part of this project wasn't creating an API.
Creating:
GET /posts
is easy.
The interesting questions appeared afterward.
Who owns the data?
One central system.
How do other applications access it?
An API.
How do I avoid repeatedly requesting unchanged information?
Caching.
How does cached information know when it becomes outdated?
Webhooks.
How do search engines understand three similar article URLs?
Canonicalization.
How do search engines discover new content?
Crawlable internal links, sitemaps, feeds, and supported notification mechanisms.
How do I make updates manageable?
One editing workflow.
That's architecture.
Not adding technologies because they're fashionable, but identifying individual problems and giving each one the smallest reasonable solution.
The Beginner-Friendly Mental Model
If you're new to backend/frontend architecture, don't memorize all of these terms.
Remember this story instead.
Database
The notebook containing the real information.
API
Someone asking to read information from the notebook.
Cache
A photocopy kept nearby so you don't have to open the notebook every time.
Webhook
Someone calling you to say the notebook changed, so your photocopy is outdated.
Cache invalidation
Throwing away the outdated photocopy.
Revalidation
Creating a fresh photocopy from the updated notebook.
Canonical URL
Marking which copy is considered the original publication.
Sitemap
Giving search engines a directory of the important pages you want them to know about.
Once those concepts make sense, the architecture stops looking complicated.
It's just several small solutions connected together.
Would I Build It This Way Again?
For this particular problem, yes.
For one small portfolio with five static articles?
Probably not.
I'd keep things simpler.
But once I had:
one dedicated blogging system,
multiple portfolio frontends,
selective distribution,
SEO requirements,
automatic synchronization,
performance requirements,
the architecture started making sense.
And that's perhaps the most useful lesson I took away from building it:
Good architecture isn't about using the most technologies.
It's about being able to answer:
Why does this component exist?
For every component in this system, I now have an answer.
The API exists because separate frontends need the data.
The cache exists because thousands of users shouldn't repeatedly ask for unchanged data.
The webhook exists because cached data needs to know when it becomes outdated.
The canonical exists because three URLs shouldn't confuse the ownership of one article.
And the central CMS exists because I never want to write the same article three times.
That's the architecture I wanted:
write once, manage once, distribute automatically.
Primary References
I based the search-engine-specific parts of this architecture on primary documentation rather than SEO folklore: