Web App Architecture:production web app 在雲端上有哪些部分

Published on Updated on Written by

前端專案在 localhost 正常執行,還不等於使用者已經能在網路上使用它。要把 web app 交到使用者手上,接下來會遇到一連串問題:網址要怎麼指向網站?HTML、CSS 和 JavaScript 要放在哪裡?需要在伺服器執行的程式要跑在哪裡?資料要存去哪裡?上線後發生錯誤,又要怎麼知道?

Web app architecture 談的就是:系統需要哪些部分、各自在解決什麼問題,以及資料如何在它們之間流動。 一個只有靜態內容的網站,可能只需要 hosting 和 CDN;如果需要登入、資料寫入、檔案上傳或背景工作,就可能會需要 auth、runtime、database、object storage 或 queue。

這篇關心的不是如何讓大型社群網站支撐十億名使用者,也不是 system design 面試裡的極端流量題,而是一個人如何把 web app 完整上線。對 solo founder 或接案工程師來說,團隊裡通常沒有另外的 SRE 或 DevOps 可以接手,因此除了理解每個元件的責任,也要知道哪些工作可以交給雲端業者的代管服務。

為什麼值得學

  • AI agent 降低了一個人完成產品的門檻。 工程師也更常需要同時處理架構設計、前後端程式與部署,不再有明確的職務分界。
  • 代管服務讓小團隊有能力維護完整系統。 現在已經有各種雲端服務,把許多原本需要專門人力的工作包成可直接使用的產品。工程師不必自己搭完所有基礎設施,但仍要知道服務替自己處理了什麼。
  • LLM agent 可以產生程式碼,卻不能替你承擔架構決策。 agent 已經能協助分析需求、建立程式碼與執行任務,但使用哪些服務、如何取捨成本與可靠度,仍需要人決定。

定位與脈絡

一句話:什麼是 web app architecture

Web app architecture 描述的是一組分工清楚、彼此連接的元件。理解架構時,可以依序問四個問題:系統有哪些元件、每個元件解決什麼問題、資料如何在它們之間流動,以及各元件要採用代管服務,還是自行建置與維運。前三個問題不太受產品更替影響;最後一個則會隨著服務內容與價格持續改變。因此,這篇著重在相對穩定的架構概念。

一個請求的完整旅程

使用者在瀏覽器輸入網址後,請求通常會沿著以下路徑取得回應:

flowchart LR
  U[User browser] -->|DNS lookup| D[DNS]
  D --> CDN[CDN / edge cache]
  CDN -->|cache hit: static asset| U
  CDN -->|cache miss / dynamic| LB[Load balancer<br/>+ TLS + WAF]
  LB --> R[Application runtime<br/>server / serverless / edge]
  R --> DB[(Managed DB)]
  R --> OBJ[(Object storage)]
  U -->|register / sign in| AUTH[Auth]
  AUTH -->|session / token| U
  R -->|verify identity| AUTH
  R --> EXT[External APIs<br/>LLM / email / payments]
  R --> Q[Queue]
  Q --> W[Background worker]
  W --> DB
  W --> EXT
  R -.-> OBS[Logs / metrics / traces<br/>alerts]
  W -.-> OBS
  R -.-> SEC[Secrets store]
  W -.-> SEC

圖上的方塊代表系統角色,不對應特定服務商。初學時不需要先認識每個角色的所有產品,更實際的做法是先選一套平台,把一個小型 web app 從部署、登入、讀寫資料一路接到檔案上傳。 等到遇到成本、功能或法規限制,再替換其中一個服務即可。只要先記得:每個角色通常都有其他替代服務,不是選定平台後就永遠不能更換。

可以先從以下四套選一套:

  • Firebase:把 hosting、runtime、database、storage 和 auth 集中在同一個開發平台,適合先理解前端如何使用 backend service。
  • Cloudflare:以 CDN、edge runtime 和 storage 為核心,適合 web app,也有許多可在 free tier 內練習的服務。
  • Vercel:從前端 framework 的部署體驗出發,hosting、function 與 CDN 整合度高;database 和 auth 通常再透過 Marketplace 接入。
  • AWS Amplify:AWS 提供給 web 與 mobile app 的整合式開發平台,將 hosting、auth、data、storage 與 function 包成比直接挑選 AWS 服務更容易入門的入口。

下表的產品名稱是「角色對照表」,不是購物清單。同一列只要選一個能解決需求的服務,不需要全部使用。架構圖只畫 DNS,因為 request 過程只會經過 DNS 解析;表格則把購買網域與設定 DNS records 合在同一列,對應實際上線時要完成的工作。

角色負責的工作先從四套平台找什麼
Domain / DNS購買網域、設定 DNS records,將 domain 指向 hostingNamecheap;Cloudflare Registrar / DNS;Vercel Domains / DNS;AWS Route 53。Firebase 需連接外部的 registrar 與 DNS
CDN將靜態資源或可快取的 HTML 放在靠近使用者的節點,也能協助抵擋 DDoSFirebase Hosting / App Hosting;Cloudflare CDN;Vercel CDN;AWS Amplify Hosting
Load balancer / 反向代理把流量分配給多個 runtime instance,並處理 TLS、健康檢查與 WAF這四套代管平台通常已經包在 hosting 或 runtime 前面,第一個專案不用另外挑產品
Application runtime執行應用程式,包括 server、serverless function 與 edge functionFirebase App Hosting / Cloud Functions;Cloudflare Workers;Vercel Functions;AWS Amplify Functions。另見 JS runtime
Database長期儲存與查詢應用程式資料Firebase Firestore / Data Connect;Cloudflare D1;Vercel Marketplace database integration;AWS Amplify Data
KV store(選用)儲存需要低延遲 key-value 讀寫的資料,例如 session 或 rate limit counter;不是泛指各層的 cacheCloudflare KV;其他平台的第一個專案通常先使用 database,真的遇到獨立 KV 的需求再加
Object storage儲存使用者上傳、靜態檔案、封存 log 與備份Firebase Cloud Storage;Cloudflare R2;Vercel Blob;AWS Amplify Storage(Amazon S3)
Queue(選用)讓 request 先回應,不讓耗時工作持續占用處理 request 的 runtime;運算仍由 background worker 執行,queue 負責排隊、重試與吸收流量尖峰Firebase task queue functions;Cloudflare Queues;Vercel Queues;AWS Amazon SQS。沒有背景工作時先不要加
Secrets store保管 API key、資料庫密碼與憑證,避免寫入 GitFirebase secrets;Cloudflare Workers secrets;Vercel sensitive environment variables;AWS Amplify secrets
Observability收集 log、metric 與 trace,並在異常時發出 alert先用各平台內建 log;需要跨前後端追蹤 error 時,再接 Sentry
Auth確認使用者身分,並處理註冊、登入與 session;資料存取權限仍須由 backend 或 access rules 強制執行Firebase Authentication;AWS Amplify Auth。CloudflareVercel 可接外部 auth service
Email / transactional 通訊寄送帳號驗證、密碼重設、收據與系統通知這通常是平台外的專門服務,例如 Resend;AWS stack 也可使用 Amazon SES
Tip

練習時,目標可以是讓最小架構完整落在 free tier,而不是把表中的角色全部加進去。 先使用平台提供的免費子網域;只有真的需要長任務時才加 queue,需要上傳檔案時才加 object storage。自有網域通常仍要向 Namecheap 之類的 registrar 支付年費。各家的「免費」條件也不同:Firebase 有些服務需要啟用 Blaze billing 才能部署,但仍提供 no-cost quota;Vercel Hobby 限個人、非商業用途;AWS Free Tier 則有帳號資格、credit 與期限限制。正式上線前,仍要請 AI 幫你重新查看各家服務的最新價格條件。

如果想用一篇文章補齊整體概念,可以讀 Jonathan Fulton 的 Web Architecture 101。文章以 Storyblocks 當時的 production stack 為例,逐段說明請求如何從 DNS 抵達資料庫。它寫於 2017 年,因此沒有涵蓋後來普及的 serverless 與 edge runtime,但基礎架構仍然適用,而且內容不綁定單一雲端服務商。

如果想看一套平台如何把這些角色組合起來,可以讀 Cloudflare 的 full-stack application 架構圖AWS Amplify 文件。這類文件會把每個角色對應到自家產品,但不代表每個角色都只能使用該公司的服務。閱讀時先辨認角色的責任與資料流,不用急著記住所有產品名稱。

Managed vs self-host:solo dev 的預設

對 solo developer 而言,沒有特殊限制時,通常先選代管服務比較實際。

  • 很多服務不是一開始設定好就結束。以資料庫為例,磁碟容量、備份、版本升級、replication 和慢查詢都要持續處理,這些工作會直接占用開發產品的時間。對仍在驗證市場需求、流量也不高的產品來說,代管服務的費用通常低於自行維運的人力成本。
  • Vendor lock-in 確實存在,但程度要看服務是否採用標準協定。採用標準協定的主流服務通常可以遷移,只是過程仍會產生工程成本和 downtime。產品還在早期時,不必先為所有假想的遷移情境增加複雜度。

自行維運通常要有明確理由,例如法規或資料主權要求、代管費用已經高到值得配置維運人力,或應用程式的查詢模式需要非常細緻的調校。如果尚未遇到這些限制,基本上都沒有必要先承擔額外的維運工作。

程式的相容設計

架構不只決定基礎設施怎麼安排,應用程式本身也要採用相容的設計,否則更換 runtime instance 或外部服務時仍然會卡住。The Twelve-Factor App 整理了十二項適合雲端部署的應用程式原則。對 solo developer 來說,以下幾項最常直接影響部署與維運:

  • Config in env:secret、資料庫 URL 與 API key 應由環境注入,不要寫死在程式碼裡,更不能提交到 Git。更多安全方面的說明,可以參考另一篇介紹 secret 管理的文章
  • Stateless processes:Process 不保存狀態,runtime instance 才能隨時增加、替換或重新啟動;需要長期保存的資料則放進 object storage 或資料庫。
  • Backing services as attached resources:把資料庫等 backing service 當作可替換的外部資源,並透過設定連接,替換服務時就不需要改寫應用程式邏輯。
  • Dev/prod parity:開發與 production 環境如果設計成差異太大,問題就要等到上線才會發現。

從能執行到能長期維護

Demo 只要能完成主要流程即可;production 系統還要降低故障機率、及早發現異常、控制成本,並在資料遺失或服務中斷時恢復運作。以下幾件事不一定難,卻很容易在趕著上線時被忽略:

  • Secret 管理
  • 系統的 observability 與 alert
  • 實際做過還原演練
  • 限制濫用與異常流量
  • 獨立的 staging 環境

實際練習

這個練習只做一個最小留言板,共分成五步。第一步先把後面四步的完整需求交給 AI,比較 Firebase、Cloudflare、Vercel、AWS Amplify,選出最適合初學者、而且能免費完成全部練習的主平台與必要外接服務;接著才依序部署靜態頁面、加入註冊登入、建立純文字留言,最後讓留言可以附圖片。每次只增加一項使用者需求,讓架構因為實際問題逐步長出來;不需要為了用到前文提過的所有角色,另外加入 queue、external API 或 email。

這是用來建立架構概念的入門練習,不是 production 選型,也不是完整的 production-ready 檢查;secret、alert、還原演練、流量限制與 staging 仍要依上一節逐項處理。練習先使用平台提供的免費子網域,不必購買網域。涉及平台功能、free tier、付款方式與超額計費時,請 agent 當下查閱官方文件,不要憑記憶回答。

五步可以各自開一個新的 session,但不能只靠對話紀錄傳遞狀態。第一步先在專案建立 docs/web-app-architecture-lab.md,之後每一步都在開始時閱讀、結束前更新。這份交接檔至少要包含:

  • 五步的固定需求與範圍。
  • 選定的主平台、外接服務、選擇理由與官方文件來源。
  • 目前完成到哪一步,以及實際使用的專案檔案與設定名稱。
  • 預計的角色與服務對照,以及每一步完成後的 Mermaid 架構圖、角色責任與變更原因。
  • 已執行的驗證、結果、未解問題與下一步。

交接檔只能記錄環境變數或 secret 的名稱,不能寫入 credential 值。更新時要保留前一步的架構圖與決策,若決策已經失效則標記原因,不要直接覆蓋。下一個 agent 也不能把交接檔當成絕對正確的事實:開始工作前仍要對照實際程式碼、部署設定與平台狀態,發現內容過期時先修正交接檔。

後面四次實作迭代都依照同一個順序進行:

  1. 理解需求:先說明目前版本為什麼無法滿足新需求。
  2. 修改架構:由自己先畫新版資料流,標出新增的角色與責任,再請 agent 訂正。
  3. 實際串接:只加入這次需要的服務,保持前一版功能繼續運作。
  4. 驗證結果:測試正常與失敗路徑,從平台介面或 log 找到證據,最後更新架構圖。

第 1 步:選擇最適合初學者的免費平台

這一步不是替 production app 選型,而是替初學者選一套容易看懂、容易操作的教學環境。先把五步的固定範圍交給 AI:同一個留言板需要 static hosting、managed auth、application runtime、managed DB 與 object storage,但不需要 queue、external API、email 或自有網域。必要條件是五步都能在免費額度內完成,而且不依賴限時試用 credit;接著才比較需要外接多少服務、文件是否清楚、設定步驟、錯誤訊息與各服務的管理介面是否適合學習。

把以下 prompt 複製到新的 session:

我要用一個最小留言板,分五步學習 web app architecture:
1. 選擇最適合初學者、能免費完成練習的主平台與必要外接服務。
2. 部署只有假留言的靜態頁面。
3. 加入使用者註冊、登入、保持登入狀態與登出。
4. 讓登入者建立純文字留言,由 runtime 驗證 session 後寫入 managed DB。
5. 讓登入者建立附一張圖片的留言,圖片放 object storage,DB 只存 metadata。
這個練習不需要 queue、external API、email、自有網域或完整的 production hardening。請先
閱讀目前專案,再依下列方式協助我選擇:
1. 用目前的官方文件比較 Firebase、Cloudflare、Vercel、AWS Amplify。逐一確認 static
hosting、auth、runtime、DB 與 object storage 能否在免費額度內完成;主平台缺少角色時,
外接服務也必須免費。不能把限時試用 credit 當成免費方案。
2. 先排除必須實際付費才能完成五步的方案。若需要綁付款方式、開啟 billing,或用量超過後
會自動收費,必須明確標示風險;初學練習優先選不用綁付款方式,或能設定硬性用量上限的方案。
3. 對剩下的方案比較初學者的學習體驗:需要建立幾個帳號、要外接多少服務、官方文件是否
有完整入門路徑、local development 與部署步驟、錯誤訊息,以及管理介面能否直接看見
auth user、DB record、runtime log 與 storage object。不要以 production scalability、
商業功能或 vendor lock-in 當主要評分標準。
4. 推薦一套最容易學習的免費主平台與必要外接服務,但先等我確認,不要建立專案或部署。
如果四套都無法符合條件,不要硬選;說明卡在哪個角色,再提出一套同樣適合初學者且能
免費完成五步的替代組合。
5. 我確認後,在專案建立 docs/web-app-architecture-lab.md,記錄固定需求、角色與服務對照、
免費方案的官方文件證據與查閱日期、選擇理由、目前步驟和下一步。這一步不要先畫最終
架構圖;Mermaid 圖要等後續每新增一項需求時再畫。只能記錄環境變數或 secret 名稱,
不能寫入任何 credential 值。
如果官方文件與你的既有知識不同,以目前官方文件為準;無法確認的地方要明確標示。

第 2 步:部署靜態留言板

先建立只有前端的留言板,畫面放幾筆寫死在程式碼裡的假留言。這一版還不能註冊,也不能真的建立留言;重點是把靜態檔案部署到雲端,觀察使用者輸入網址後,瀏覽器如何取得 HTML、CSS 與 JavaScript。

把以下 prompt 複製到新的 session:

繼續 docs/web-app-architecture-lab.md 裡規劃的最小留言板。這是第 2 步:把只有前端的
靜態留言板部署到雲端。請擔任我的教練,不要一開始就替我畫好架構:
1. 先閱讀現有程式碼、部署設定與 docs/web-app-architecture-lab.md,確認交接內容和專案
實際狀態一致。沿用已確認的主平台與外接服務,不要重新選型。
2. 讓我先畫出 Browser、DNS、CDN / hosting 之間的 request path,再幫我訂正。特別指出
使用平台免費子網域時,DNS、TLS 與 CDN 哪些由平台代為處理。
3. 建立只有前端的留言板,放幾筆寫死的假留言,部署到平台免費子網域。不要加入 auth、
runtime、database、object storage 或自有網域。
4. 部署後跟我一起驗證公開網址、HTTPS 與靜態檔案是否正常,再問我每個架構角色解決什麼
問題,以及哪些維運責任已經交給平台。
完成後更新交接檔的目前步驟、實際檔案、部署網址、驗證結果、未解問題與下一步,並新增
這個練習的第一張架構圖。平台資訊只採用官方文件;交接檔不得寫入 credential 值。

第 3 步:讓使用者註冊與登入

沿用同一個留言板與平台,加入 managed auth。登入後只要顯示目前的使用者與登出按鈕,這一版仍然不能建立留言。把範圍停在身分與 session,可以看清楚 auth service 儲存的是使用者身分,不是應用程式的留言資料。

把以下 prompt 複製到新的 session:

繼續 docs/web-app-architecture-lab.md 裡的留言板與既有 stack。這是第 3 步:加入註冊、
登入與登出,但還不要建立留言或加入 application database。請擔任我的教練:
1. 先閱讀交接檔、現有程式碼與部署設定,核對第 2 步真的完成,再讓我說明現有靜態架構
為什麼不知道使用者是誰,並畫出 Browser、hosting、auth service 與 session / token
之間的資料流,再幫我訂正。
2. 依平台目前的官方文件接上 managed auth,完成註冊、登入、保持登入狀態與登出。不要
自行設計密碼儲存或自製 auth。
3. 登入後顯示目前的使用者,登出後回到未登入狀態;不要在這輪加入留言寫入、DB 或圖片。
4. 跟我一起驗證註冊、登入、重新整理與登出,並從 auth service 的管理介面確認使用者存在。
最後問我 auth service、session 與 application data 各自負責什麼。
完成後更新交接檔的目前步驟、實際檔案、設定名稱、驗證結果、未解問題與下一步,並新增
本步完成後的架構圖,不要覆蓋前一步版本。平台行為、安全設定與限制只採用官方文件;
交接檔不得寫入 credential 值。

第 4 步:建立純文字留言

登入之後,下一項需求是讓使用者建立純文字留言。這一輪加入 application runtime 與 managed DB:runtime 驗證 session、處理留言規則,再把資料寫入 DB。為了清楚練習前後端的權限邊界,前端不能自行指定留言作者;runtime 必須從驗證過的 session 取得 user ID。

把以下 prompt 複製到新的 session:

繼續 docs/web-app-architecture-lab.md 裡的留言板與既有 stack。這是第 4 步:讓已登入的
使用者建立純文字留言,並在重新整理或重新部署後保留資料。請擔任我的教練:
1. 先閱讀交接檔、現有程式碼與部署設定,核對第 3 步真的完成,再讓我說明 auth 為什麼
不能代替 application database,並畫出 Browser、runtime、auth 與 DB 之間的資料流,
再幫我訂正。
2. 加入平台的 application runtime 與 managed DB。留言至少保存文字、作者 ID 與由
backend 產生的建立時間;不要加入圖片、object storage 或其他服務。
3. Backend 必須驗證 session,並從驗證結果取得作者 ID,不能相信前端傳來的 userId。
前端隱藏按鈕只能改善使用體驗,不能當成權限控制。未登入者直接呼叫寫入端點時,backend
必須拒絕請求。
4. 跟我一起驗證:登入者可以留言、未登入者會被拒絕、假造 userId 不能冒用別人、DB
看得到正確資料,而且重新部署 runtime 後留言仍然存在。最後讓我更新架構圖並說明為什麼
runtime 應保持 stateless。
完成後更新交接檔的目前步驟、實際檔案、設定名稱、驗證結果、未解問題與下一步,並新增
本步完成後的架構圖,不要覆蓋前一步版本。平台 API、session 驗證與 DB 用法只採用官方
文件;交接檔不得寫入 credential 值。

第 5 步:建立附圖片的留言

最後讓留言可以附一張圖片,並加入 object storage。圖片本體放進 object storage,DB 只保存留言文字、作者 ID、object key 或 URL 等結構化資料。這次要特別觀察:一次建立留言會跨越 DB 與 object storage,任一邊失敗時都可能留下不完整資料。

把以下 prompt 複製到新的 session:

繼續 docs/web-app-architecture-lab.md 裡的留言板與既有 stack。這是第 5 步:讓已登入的
使用者建立一則附圖片的留言。請擔任我的教練:
1. 先閱讀交接檔、現有程式碼與部署設定,核對第 4 步真的完成,再讓我說明為什麼不應直接
把圖片本體塞進 application DB,並畫出 Browser、runtime、auth、DB 與 object storage
之間的資料流,再幫我訂正。
2. 依平台目前官方建議的上傳方式加入 object storage。圖片本體放 storage;DB 只保存
留言、作者與 object key / URL 等必要 metadata。先限制為每則一張圖片,不加入縮圖、
圖片處理、queue 或 external API。
3. 上傳與讀取權限必須由 backend 或 storage access rules 強制執行,不能只靠前端隱藏
上傳按鈕。限制允許的檔案類型與大小,也不要把 storage credential 放進前端程式碼。
4. 跟我一起驗證:正常留言、未登入上傳、超過大小限制、檔案上傳失敗等路徑。確認失敗時
不會留下指向不存在圖片的留言,並討論刪除留言時是否也要刪除圖片。最後更新完整架構圖。
完成後更新交接檔的目前步驟、實際檔案、設定名稱、驗證結果與未解問題,新增本步完成後的
架構圖,不要覆蓋前一步版本,並將下一步改成整體回顧。平台的 storage、權限與上傳限制只
採用官方文件;交接檔不得寫入 credential 值。

完成五步後,回頭比較交接檔裡的架構變化:平台選擇先確認整條路徑可行;靜態頁面只需要 hosting;使用者身分帶來 auth 與 session;長期保存留言需要 runtime 與 DB;檔案上傳才需要 object storage。這個練習的目的不是把所有雲端服務都串過一次,而是能說明每個角色是因為哪一項需求才加入,以及它替系統承擔了什麼責任。能完成主要流程只代表 demo 可以運作;正式提供給使用者前,還要回到上一節檢查觀測、還原、流量限制與環境隔離。

延伸主題

這篇先交代整體架構,沒有深入每項服務的實作細節。接下來要研究資料庫、queue、observability 或部署流程,可以由目前專案最常遇到的問題決定;不需要照固定順序把所有主題學完。

圖表瀏覽器

100%