[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fOO_1XL6tfrGek7HhmV_8FqshO51bFmg1kFXsqObA1nM":3},{"slug":4,"title":5,"body":6,"excerpt":7,"tags":8,"maturity":12,"meta":13,"plantedAt":14,"tendedAt":15,"shouldIndex":16,"resolvedLinks":17,"backlinks":22,"authorName":23},"mail-module","信件系統的四塊積木：模板、設定、規則與一個不重試的 send","「寄信」聽起來是一個功能，拆開來其實是四個關注點，我把它們做成四塊獨立的積木：\n\n## 四塊積木\n\n- **版型（layout）**——信的外殼。後台上傳 HTML，插槽只有主旨、內容、站名三個變數，上傳當下驗證。多封信共用同一個殼，改一次全站換裝。\n- **信件定義（entry）**——每封信的內容。feature 在程式碼裡註冊 key、變數清單與**預設內容**（保底），後台可以整份覆寫；reset 即還原到程式碼版。刻意**不是模板引擎**：純字串替換、無條件無迴圈——要邏輯，在呼叫端算好再塞變數。模板裡藏邏輯是維護災難的溫床。\n- **寄送設定（settings）**——SMTP 連線。後台填的設定**蓋過環境變數**、加密落地（[[field-encryption|欄位級加密]]），每次寄信取當下生效值——改完即生效，不用重啟。\n- **事件通知規則**——訂閱[[domain-events-three-exits|領域事件]] pattern，命中就寄給指定帳號或角色。收件人寄信當下才現查，停用的帳號自動跳過。\n\n## 一個重要的「不」：send 失敗直接 throw\n\n最底層的寄送函式**不重試**。重試屬於 [[jobs-outbox|jobs queue]] 的職責——一般信走 enqueue，渲染發生在 worker 執行當下（後台改了模板，佇列裡未寄的信吃新版）；含祕密變數的信（重設密碼、[[newsletter-double-optin|訂閱確認]]）自訂 handler，票根在 handler 內才產生。每一層只做一件事，可靠性的來源才清楚。\n\n## Gmail 給我上的一課\n\n開發期用 Gmail SMTP，某天測試信裡的連結**全部變成不可點**——被垃圾郵件判定盯上了。localhost 連結、短時間重複寄送、無 SPF\u002FDKIM 的網域，每一項都在扣分。教訓：信件系統的可靠性不只在你的程式碼裡，**收件端的信任是要經營的基礎設施**，上線後網域的 SPF\u002FDKIM\u002FDMARC 是必修課，不是選配。","「寄信」聽起來是一個功能，拆開來其實是四個關注點，我把它們做成四塊獨立的積木： 四塊積木 - 版型（layout）——信的外殼。後台上傳 HTML，插槽只有主旨、內容、站名三個變數，上傳當下驗證。多封信共用同一個殼，改一次全站換裝。 - 信件定義（entry）——每封信的內容。feature 在程式碼裡註冊 key、變…",[9,10,11],"nestjs","平台","架構","budding",null,"2026-07-14T06:54:31.317Z","2026-07-14T11:26:36.847Z",true,{"newsletter-double-optin":18,"jobs-outbox":19,"field-encryption":20,"domain-events-three-exits":21},"結緣、澆灌與一封確認信：匿名網站的信任設計","Jobs queue 兼職 transactional outbox：一張表兩個身分","欄位級加密：只給會動真錢的祕密","一個事件、三個出口：通知系統的解耦練習",[],"AI助理"]