Golang Model 字段自動化校驗(yàn)設(shè)計(jì)方案
背景
在我們?nèi)粘i_發(fā)中,不可避免的總要去進(jìn)行各種參數(shù)校驗(yàn),但是如果在某個場景中,要校驗(yàn)的字段非常多,并且在其中還有耦合關(guān)系,那么我們手寫校驗(yàn)邏輯就變得非常的低效且難以維護(hù)。本篇文檔就基于 DDD 領(lǐng)域模型設(shè)計(jì)的思想下,提供自動化的校驗(yàn)?zāi)P妥侄巍?/p>
常見的字段校驗(yàn)方式
數(shù)據(jù)校驗(yàn)在業(yè)務(wù)邏輯代碼中有著至關(guān)重要的作用,關(guān)系到整個后續(xù)業(yè)務(wù)是否可以正常運(yùn)行。對參數(shù)的校驗(yàn)根據(jù)其具體業(yè)務(wù)邏輯與場景,可以分為字段校驗(yàn)、依賴校驗(yàn)、功能校驗(yàn)與邏輯校驗(yàn)四個部分。
字段校驗(yàn)
字段校驗(yàn)是最常見的校驗(yàn)類型。例如:商品名稱不能超過多少個字符,商品狀態(tài)必須是有效等。
func (e *Shop) ValidateShopName() error { if e.Name != nil && e.Name == "" { return errors.New("商品名稱不能為空。") } if e.Name != nil && utf8.RuneCountInString(e.Name) > constant.MaxShopNameLength { return errors.Errorf("商品名稱長度為 %d, 不能超過 %d ", utf8.RuneCountInString(e.Name), constant.MaxShopNameLength) } return nil }
依賴校驗(yàn)
依賴校驗(yàn),顧名思義是在業(yè)務(wù)邏輯中依賴了其他模塊。例如,在創(chuàng)建商品信息時,要校驗(yàn)一下商品依賴的商家或供應(yīng)商等信息是否合法。
func (e *Shop) ValidateMerchant() error { // 在此方法中可能需要進(jìn)行外部調(diào)用或者查詢 DB 的操作。 if e.HasInvalidMerchant() { return errors.New("商家信息存在異常") } return nil }
功能校驗(yàn)
功能校驗(yàn)例如用戶是否有權(quán)限發(fā)布商品、商品信息是否與其他商品存在沖突等。
func (e *Shop) ValidateUserPermission() error { if e.UserCreateShopWithoutPermission() { return errors.New("用戶無權(quán)限創(chuàng)建商品") } return nil }
邏輯校驗(yàn)
邏輯校驗(yàn)主要是一些具體的業(yè)務(wù)邏輯。例如在下架商品時,校驗(yàn)是否有新用戶下單等。
func (e *Shop) ValidateCloseShop() error{ if e.InvalidShopStatus() { return errors.New("商品已下架") } if e.ExistShopTicket() { return errors.New("有正在進(jìn)行的訂單信息,無法下架") } return nil }
上面我們列出來常見的四種校驗(yàn)方式,當(dāng)我們在一個復(fù)雜且龐大的業(yè)務(wù)場景需要把各種各樣的校驗(yàn)放在一起去校驗(yàn)時,我們不得不編寫一個龐大的校驗(yàn)函數(shù),把這些單點(diǎn)的校驗(yàn)函數(shù)聚合起來,更有甚者都沒有進(jìn)行子邏輯校驗(yàn)的函數(shù)區(qū)分,就是第一個大函數(shù),把各種各樣的校驗(yàn)邏輯代碼寫到一個函數(shù)中,那么長此以往,校驗(yàn)邏輯就會非常復(fù)雜,無法迭代。
func (e *Shop) ValidateCreateShop() error { if err = e.ValidateShopName(); err != nil { return err } if err = e.ValidateDescrption(); err != nil { return err } if err = e.ValidateImage(); err != nil { return err } if err = e.ValidateMerchant(); err != nil { return err } if err = e.ValidateUserPermission(); err != nil { return err } if err = e.ValidateCloseShop(); err != nil { return err } return nil }
自動化校驗(yàn)
type Validator struct { FieldNames []string // 需要更新的字段 ValidateNames []string // 需要校驗(yàn)的字段列表 ValidateFuncList []Func() error // 校驗(yàn)函數(shù)列表 } func (v *Validator) Validate() error { for _, validate := range v.validateFuncList { if err := validate(); err != nil { return err } } return nil } // GetFields2ValidateFuncMap 各個字段的校驗(yàn)函數(shù)在這里擴(kuò)展,在調(diào)用 register 函數(shù)時,會自動注冊 func (a *Aggregate) GetFields2ValidateFuncMap() map[string]func() error { return map[string]func() error { constant.ShopForCreate: a.Shop.ValidateCreateShop, constant.ShopForUpdate: a.Shop.ValidateUpdateShop, constant.ShopCanStart: a.Shop.CanStart, // ... 等等各種校驗(yàn)都可以在這里定義一個聚合函數(shù)列表 } } func DTOToAgg(dto *DTO.Shop) (*shop.Aggregate, error) { baseShop := base.NewBaseShop() // 先把傳參 model 轉(zhuǎn)化成領(lǐng)域數(shù)據(jù) if err = copier.Copy(baseShop, dto); err != nil { return nil, errors.Wrap(err, err.Error()) } // New 一個聚合類 shopAgg := shop.NewShopAggregate(baseShop) // 獲取本次傳給領(lǐng)域?qū)ο蟮淖侄?,以及加載要校驗(yàn)的字段 setFields := GetSetOptionalFields(*dto) var validateName []string for _, field := range setFields { validateName = append(validateName, field) } shopAgg.SetUpdateFields(setFields) // 注冊 validate 函數(shù) shopAgg.RegisterValidator(validateName) return shopAgg, nil } // 執(zhí)行校驗(yàn)函數(shù) func (v *Validator) ValidateMultipleFields(ctx context.Context) error { for _, validate := range v.validateFuncList { if err := validate(); err != nil { return err } } return }
簡單來描述自動校驗(yàn)分為以下幾個步驟:
- 在接收傳參的轉(zhuǎn)換函數(shù)中,先把本次請求傳入的字段拿到,并且注冊這些字段對應(yīng)的校驗(yàn)函數(shù)。
- 進(jìn)入到業(yè)務(wù)邏輯處理的函數(shù)中,再次增加一些當(dāng)前業(yè)務(wù)場景需要的特殊校驗(yàn)函數(shù)。
- 依次執(zhí)行校驗(yàn)函數(shù),觀察是否有報(bào)錯。
到此這篇關(guān)于Golang Model 字段自動化校驗(yàn)設(shè)計(jì)的文章就介紹到這了,更多相關(guān)Golang Model 字段校驗(yàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言編程實(shí)現(xiàn)支持六種級別的日志庫?
這篇文章主要為大家介紹了使用Golang編寫一個支持六種級別的日志庫示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05Go到底能不能實(shí)現(xiàn)安全的雙檢鎖(推薦)
這篇文章主要介紹了Go到底能不能實(shí)現(xiàn)安全的雙檢鎖,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-05-05