Golang Model 字段自動化校驗設計方案
背景
在我們?nèi)粘i_發(fā)中,不可避免的總要去進行各種參數(shù)校驗,但是如果在某個場景中,要校驗的字段非常多,并且在其中還有耦合關系,那么我們手寫校驗邏輯就變得非常的低效且難以維護。本篇文檔就基于 DDD 領域模型設計的思想下,提供自動化的校驗模型字段。
常見的字段校驗方式
數(shù)據(jù)校驗在業(yè)務邏輯代碼中有著至關重要的作用,關系到整個后續(xù)業(yè)務是否可以正常運行。對參數(shù)的校驗根據(jù)其具體業(yè)務邏輯與場景,可以分為字段校驗、依賴校驗、功能校驗與邏輯校驗四個部分。
字段校驗
字段校驗是最常見的校驗類型。例如:商品名稱不能超過多少個字符,商品狀態(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è)務邏輯中依賴了其他模塊。例如,在創(chuàng)建商品信息時,要校驗一下商品依賴的商家或供應商等信息是否合法。
func (e *Shop) ValidateMerchant() error {
// 在此方法中可能需要進行外部調(diào)用或者查詢 DB 的操作。
if e.HasInvalidMerchant() {
return errors.New("商家信息存在異常")
}
return nil
}功能校驗
功能校驗例如用戶是否有權限發(fā)布商品、商品信息是否與其他商品存在沖突等。
func (e *Shop) ValidateUserPermission() error {
if e.UserCreateShopWithoutPermission() {
return errors.New("用戶無權限創(chuàng)建商品")
}
return nil
}邏輯校驗
邏輯校驗主要是一些具體的業(yè)務邏輯。例如在下架商品時,校驗是否有新用戶下單等。
func (e *Shop) ValidateCloseShop() error{
if e.InvalidShopStatus() {
return errors.New("商品已下架")
}
if e.ExistShopTicket() {
return errors.New("有正在進行的訂單信息,無法下架")
}
return nil
}上面我們列出來常見的四種校驗方式,當我們在一個復雜且龐大的業(yè)務場景需要把各種各樣的校驗放在一起去校驗時,我們不得不編寫一個龐大的校驗函數(shù),把這些單點的校驗函數(shù)聚合起來,更有甚者都沒有進行子邏輯校驗的函數(shù)區(qū)分,就是第一個大函數(shù),把各種各樣的校驗邏輯代碼寫到一個函數(shù)中,那么長此以往,校驗邏輯就會非常復雜,無法迭代。
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
}自動化校驗

type Validator struct {
FieldNames []string // 需要更新的字段
ValidateNames []string // 需要校驗的字段列表
ValidateFuncList []Func() error // 校驗函數(shù)列表
}
func (v *Validator) Validate() error {
for _, validate := range v.validateFuncList {
if err := validate(); err != nil {
return err
}
}
return nil
}
// GetFields2ValidateFuncMap 各個字段的校驗函數(shù)在這里擴展,在調(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,
// ... 等等各種校驗都可以在這里定義一個聚合函數(shù)列表
}
}
func DTOToAgg(dto *DTO.Shop) (*shop.Aggregate, error) {
baseShop := base.NewBaseShop()
// 先把傳參 model 轉(zhuǎn)化成領域數(shù)據(jù)
if err = copier.Copy(baseShop, dto); err != nil {
return nil, errors.Wrap(err, err.Error())
}
// New 一個聚合類
shopAgg := shop.NewShopAggregate(baseShop)
// 獲取本次傳給領域?qū)ο蟮淖侄危约凹虞d要校驗的字段
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í)行校驗函數(shù)
func (v *Validator) ValidateMultipleFields(ctx context.Context) error {
for _, validate := range v.validateFuncList {
if err := validate(); err != nil {
return err
}
}
return
}
簡單來描述自動校驗分為以下幾個步驟:
- 在接收傳參的轉(zhuǎn)換函數(shù)中,先把本次請求傳入的字段拿到,并且注冊這些字段對應的校驗函數(shù)。
- 進入到業(yè)務邏輯處理的函數(shù)中,再次增加一些當前業(yè)務場景需要的特殊校驗函數(shù)。
- 依次執(zhí)行校驗函數(shù),觀察是否有報錯。
到此這篇關于Golang Model 字段自動化校驗設計的文章就介紹到這了,更多相關Golang Model 字段校驗內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

