# README
是的,最佳做法 是: 1. 各模組 (parser, alert, notify 等) 從 config 取得各自的 config 設定 2. 將 config 當作全域變數 (Singleton) 或 Service 結構體的一部分,傳遞給各個函式,以確保所有函式可以存取相同的設定。 3. 透過 Service 結構管理 config,避免函式間直接存取全域變數,確保模組化與測試性。
✅ 最佳架構
📌 目錄結構
🔥 config 模組
📌 config/config.go
package config
import ( "fmt" "github.com/spf13/viper" "sync" )
// Config 結構存放所有設定 type Config struct { Alert AlertConfig Parser ParserConfig Notify NotifyConfig }
// AlertConfig 告警模組設定 type AlertConfig struct { Threshold int LogPath string }
// ParserConfig 解析器模組設定 type ParserConfig struct { InputPath string LogLevel string }
// NotifyConfig 通知模組設定 type NotifyConfig struct { RetryLimit int EmailSMTP string }
var ( instance *Config once sync.Once )
// InitConfig 讀取 YAML 設定 func InitConfig(configPath string) *Config { once.Do(func() { v := viper.New() v.SetConfigFile(configPath) v.AutomaticEnv() // 支援環境變數
if err := v.ReadInConfig(); err != nil {
panic(fmt.Sprintf("❌ 讀取設定檔失敗: %v", err))
}
instance = &Config{
Alert: AlertConfig{
Threshold: v.GetInt("alert.threshold"),
LogPath: v.GetString("alert.log_path"),
},
Parser: ParserConfig{
InputPath: v.GetString("parser.input_path"),
LogLevel: v.GetString("parser.log_level"),
},
Notify: NotifyConfig{
RetryLimit: v.GetInt("notify.retry_limit"),
EmailSMTP: v.GetString("notify.email_smtp"),
},
}
})
return instance
}
// GetConfig 取得設定 func GetConfig() *Config { return instance }
🔥 Service 結構體傳遞 config
📌 alert/service.go
package alert
import ( "fmt" "shared-lib/config" )
// AlertService 管理告警邏輯 type AlertService struct { Config config.AlertConfig }
// NewAlertService 初始化 Service 並傳入設定 func NewAlertService(cfg config.AlertConfig) *AlertService { return &AlertService{Config: cfg} }
// 檢查告警規則 func (s *AlertService) CheckAlert() { fmt.Printf("🚨 [Alert] 閾值: %d, 日誌: %s\n", s.Config.Threshold, s.Config.LogPath) }
📌 parser/service.go
package parser
import ( "fmt" "shared-lib/config" )
// ParserService 管理解析邏輯 type ParserService struct { Config config.ParserConfig }
// NewParserService 初始化 Service 並傳入設定 func NewParserService(cfg config.ParserConfig) *ParserService { return &ParserService{Config: cfg} }
// 解析日誌 func (s *ParserService) ParseLog() { fmt.Printf("📜 [Parser] 解析日誌: %s, 等級: %s\n", s.Config.InputPath, s.Config.LogLevel) }
📌 notify/service.go
package notify
import ( "fmt" "shared-lib/config" )
// NotifyService 負責處理通知 type NotifyService struct { Config config.NotifyConfig }
// NewNotifyService 初始化通知服務 func NewNotifyService(cfg config.NotifyConfig) *NotifyService { return &NotifyService{Config: cfg} }
// 發送通知 func (s *NotifyService) SendNotification() { fmt.Printf("📩 [Notify] SMTP: %s, 重試次數: %d\n", s.Config.EmailSMTP, s.Config.RetryLimit) }
🔥 iPOC 啟動時讀取 config,並傳遞給各個 Service
📌 main.go
package main
import ( "shared-lib/config" "shared-lib/alert" "shared-lib/parser" "shared-lib/notify" )
func main() { // 1️⃣ 讀取設定檔 cfg := config.InitConfig("/etc/bimap-ipoc/config.yml")
// 2️⃣ 初始化各個 Service
alertService := alert.NewAlertService(cfg.Alert)
parserService := parser.NewParserService(cfg.Parser)
notifyService := notify.NewNotifyService(cfg.Notify)
// 3️⃣ 啟動模組
alertService.CheckAlert()
parserService.ParseLog()
notifyService.SendNotification()
}
🚀 這樣的設計有什麼好處?
✅ 各模組仍然「獨立」,但共用 config • alert, parser, notify 彼此不依賴,但可以統一讀取設定值。
✅ 不直接用全域變數,而是 Service 傳遞 config • 避免循環依賴,config 只在初始化時傳遞,不會在模組間互相影響。
✅ config 變更後,只要重新讀取設定檔即可生效 • 透過 config.InitConfig(),可以支援動態加載。
✅ 適用於 Docker/Kubernetes • config 允許環境變數 (viper.AutomaticEnv()),可以輕鬆透過 Docker Compose 或 K8s ConfigMap 來設定。
🚀 結論
📌 各個服務 (parser, alert, notify) 應該透過 config 模組讀取自己的 config,並以 Service 結構管理配置參數 📌 避免全域變數,讓 Service 透過參數傳遞 config,確保模組獨立性 📌 這樣 iPOC 就能靈活適應不同環境,且易於維護與擴展! 🚀