三個環境變數的哲學:memory 預設、redis 可換
平台的快取與分散式鎖各自抽象成 port,背後兩套實作:in-memory 與 redis,用環境變數切換。這個設計的核心信念是:fresh clone 必須零外部依賴就能跑起來。
預設 memory 的理由
新人(或三個月後的自己)clone 下來,npm install 完就該能啟動——不需要先架 redis、不需要 docker compose 起一排容器。CACHE_DRIVER 與 LOCK_DRIVER 預設 memory,redis 完全不在場:連 redis client 都只在設定了 REDIS_URL 時才註冊,下游模組用 optional 注入,redis 缺席是非事件,健康檢查端點也只在它在場時才多一個指標。
「開發環境的擬真度」與「上手的摩擦力」是一組交換——我選擇把摩擦力壓到零,用 port 抽象保證擬真度的債之後還得起。
但 memory 鎖有一個致命邊界
in-memory 的鎖是一個 Map——它只存在於單一程序的記憶體裡。PM2 cluster 開兩個 worker、或 k8s 跑兩個 pod,兩邊各自持有「同一把鎖」,互斥蕩然無存。jobs worker 靠鎖保證多實例下不重複執行,這個場景下 memory 鎖等於沒鎖。
所以規則寫得很死:任何非單程序的部署,LOCK_DRIVER 必須是 redis。這種「開發時安全、部署時炸」的陷阱,文件寫三遍都不嫌多——它不會在任何測試裡現形,只會在生產環境的併發裡現形。
Port 抽象的複利
因為所有消費者只認識 ICacheService / ILockService,切換 driver 是純設定變更、零程式碼改動。排程觀測寫進快取後跨 pod 一致性自動跟上,就是這個抽象付的複利——好的邊界讓後來的功能免費繼承正確性。
讀到這裡若有一點收穫,替這顆種子澆一次水——匿名、一人一次。