重點摘要
面試或口試最常被問的一句是「你的系統架構長怎樣?」。好的回答不是把用到的技術名詞唸一遍,而是分層講清楚:每一層解決什麼問題、為什麼是它。這篇把我兩套醫療系統共用的架構拆成四層,用一句話講每層的職責,並附上「為什麼這樣選」。也是寫給我自己 —— 讓我照著就能把架構講順。
先給一張總圖
使用者瀏覽器
│ ① 本地先算(原始資料不出裝置)
▼
Cloudflare Worker(全球邊緣節點)
│ ② 路由 / 認證 / 呼叫外部服務
▼
Supabase(PostgreSQL + Auth)
│ ③ 資料儲存,權限寫在資料庫層(RLS)
▼
(需要時)Gemini API
④ 只做「翻譯」,只看到去識別化的結構化結果
講架構的訣竅:從使用者那端往後端講,一層一層說「這裡做什麼、為什麼不放到別層」。下面逐層拆。
第一層:瀏覽器 —— 能本地算的就不要送出去
職責: 使用者輸入的原始資料(體檢數值等),先在瀏覽器本地完成能算的部分。
以民眾端為例:45 項體檢指標的正常/異常判讀、KDIGO 分期計算,全部在瀏覽器本地跑完,原始數值不送出裝置。往後端送的,只有「已經算好的結論」。
為什麼: 這是「隱私先行」最具體的實踐 —— 資料離開裝置的量越少,能外洩的就越少。而且判讀邏輯是固定規則,本來就不需要伺服器介入。
第二層:Cloudflare Worker —— 邊緣的門房
職責: 一個跑在全球邊緣節點的輕量後端,負責路由、認證、以及代替前端去呼叫外部服務(這樣 API 金鑰不會暴露在前端)。
為什麼是 Worker 不是傳統伺服器:
- 冷啟動幾乎為零、全球低延遲 —— 使用者在哪,就近的節點回應。
- 免費額度對小規模很夠,月成本能壓到 $0。
- 每個請求都是全新環境,不會殘留上一個使用者的狀態 —— 對「資料隔離至上」的醫療場景,少了一整類詭異 bug。
金鑰的處理是關鍵:Gemini、Supabase 的高權限金鑰只存在 Worker 的執行環境秘密裡,永遠不出現在前端、git 歷史或 log。前端有任何漏洞,都拿不到後端權限。
(更完整的選型理由在為什麼選 Cloudflare Workers 不選 AWS Lambda。)
第三層:資料庫 —— 權限寫在這裡,不寫在程式
職責: 用 Supabase(內含 PostgreSQL)存帳號、病歷、處方等資料。關鍵是:「誰能看哪一筆」的規則,寫在資料庫本身,用 RLS(Row Level Security)。
診所端有六種角色(醫師、護理師、藥師…),共 14 張表、29 條 RLS 規則。應用層幾乎不用寫「如果是醫師才…」這種判斷 —— 資料庫會自動只回傳每個人有權看的資料。
為什麼放資料庫層而不是程式層: 因為「安全落在資料庫層」意味著 —— 就算應用層被攻破、有人繞過程式直接打資料庫,權限規則還是擋著。防線在最底層,才擋得住最壞的情況。
(白話版在白話 Row Level Security,進階取捨在為什麼選 RLS 而不是應用層權限。)
第四層:語言模型 —— 只當翻譯,不做判斷
職責: 需要把冷冰冰的判定結果轉成一般人讀得懂的話時,才呼叫 Gemini。而且它只看到去識別化的結構化結論(例如「CKD G3a」),看不到原始的個人數值。
為什麼把 LLM 限制得這麼死: 因為「規則優於 LLM」。臨床判定交給可解釋、可稽核的規則引擎;LLM 會有幻覺,只能負責「把已經確定的結論翻成白話」這種低風險的事。這樣同時降低了幻覺風險和隱私風險 —— 一個設計決定解掉兩個問題。
(完整架構在「先規則後 LLM」的三層架構。)
把四層串成一句話
如果只有 30 秒,我會這樣講:
「原始資料在瀏覽器本地就先算完、盡量不送出去;邊緣的 Worker 只做路由和認證、把金鑰藏好;資料存在 PostgreSQL、而且權限規則寫在資料庫層,應用層破了也擋得住;需要講白話時才叫語言模型,而且只給它去識別化的結論、只讓它翻譯不讓它判斷。四個選擇背後是同一個主張 —— 隱私先行、規則優於 LLM、安全落在資料庫層。」
架構講得好不好,不在於用了多潮的技術,而在於每一層都答得出「你為什麼把這件事放在這裡」。
延伸閱讀
- ExClinCalc Case Study ── 這套架構在診所端的完整實作與截圖
- ClinCalc Case Study ── 民眾端的本地優先計算
- CI/CD 白話拆解 ── 這套系統怎麼從 git push 自動上線