go-validator
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGo Validator
Go 验证器
Generate validator files for GO modular architecture conventions.
为Go模块化架构规范生成验证器文件。
Two-File Pattern
双文件模式
Every validator requires two files:
- Port interface:
internal/modules/<module>/ports/<validator_name>_validator.go - Validator implementation:
internal/modules/<module>/validator/<validator_name>_validator.go
每个验证器都需要两个文件:
- 端口接口:
internal/modules/<module>/ports/<validator_name>_validator.go - 验证器实现:
internal/modules/<module>/validator/<validator_name>_validator.go
Port File Structure
端口文件结构
The port file contains only the interface definition with its documentation comment.
Example structure:
go
package ports
// PasswordValidator validates password strength according to security policies.
type PasswordValidator interface {
Validate(password string) error
}端口文件仅包含接口定义及其文档注释。
示例结构:
go
package ports
// PasswordValidator validates password strength according to security policies.
type PasswordValidator interface {
Validate(password string) error
}Validator File Structure
验证器文件结构
The validator implementation file follows this order:
- Package imports
- Constants - validation rules, thresholds, limits
- Struct definition - the validator implementation struct
- Interface assertion - compile-time check with
var _ ports.XxxValidator = (*XxxValidator)(nil) - Constructor - function
NewXxxValidator - Methods - validation methods (e.g., )
Validate
Example structure:
go
package validator
import (
"github.com/cristiano-pacheco/pingo/internal/modules/<module>/errs"
"github.com/cristiano-pacheco/pingo/internal/modules/<module>/ports"
)
// 1. Constants
const (
minLength = 8
maxLength = 128
)
// 2. Struct definition
type PasswordValidator struct{}
// 3. Interface assertion
var _ ports.PasswordValidator = (*PasswordValidator)(nil)
// 4. Constructor
func NewPasswordValidator() *PasswordValidator {
return &PasswordValidator{}
}
// 5. Methods
func (v *PasswordValidator) Validate(password string) error {
// validation logic
return nil
}验证器实现文件遵循以下顺序:
- 包导入
- 常量 - 验证规则、阈值、限制
- 结构体定义 - 验证器实现结构体
- 接口断言 - 使用进行编译时检查
var _ ports.XxxValidator = (*XxxValidator)(nil) - 构造函数 - 函数
NewXxxValidator - 方法 - 验证方法(如)
Validate
示例结构:
go
package validator
import (
"github.com/cristiano-pacheco/pingo/internal/modules/<module>/errs"
"github.com/cristiano-pacheco/pingo/internal/modules/<module>/ports"
)
// 1. Constants
const (
minLength = 8
maxLength = 128
)
// 2. Struct definition
type PasswordValidator struct{}
// 3. Interface assertion
var _ ports.PasswordValidator = (*PasswordValidator)(nil)
// 4. Constructor
func NewPasswordValidator() *PasswordValidator {
return &PasswordValidator{}
}
// 5. Methods
func (v *PasswordValidator) Validate(password string) error {
// validation logic
return nil
}Port Interface Structure
端口接口结构
Location:
internal/modules/<module>/ports/<validator_name>_validator.gogo
package ports
// PasswordValidator validates password strength according to security policies.
type PasswordValidator interface {
Validate(password string) error
}位置:
internal/modules/<module>/ports/<validator_name>_validator.gogo
package ports
// PasswordValidator validates password strength according to security policies.
type PasswordValidator interface {
Validate(password string) error
}Validator Variants
验证器变体
Stateless validator (no dependencies)
无状态验证器(无依赖)
Most validators are stateless utilities with no external dependencies.
go
type EmailValidator struct{}
func NewEmailValidator() *EmailValidator {
return &EmailValidator{}
}
func (v *EmailValidator) Validate(email string) error {
// Validation logic
return nil
}大多数验证器是无外部依赖的无状态工具。
go
type EmailValidator struct{}
func NewEmailValidator() *EmailValidator {
return &EmailValidator{}
}
func (v *EmailValidator) Validate(email string) error {
// Validation logic
return nil
}Stateful validator (with dependencies)
有状态验证器(带依赖)
Use when validation requires external data or configuration.
go
type UsernameValidator struct {
userRepo ports.UserRepository
minLen int
maxLen int
}
func NewUsernameValidator(
userRepo ports.UserRepository,
minLen int,
maxLen int,
) *UsernameValidator {
return &UsernameValidator{
userRepo: userRepo,
minLen: minLen,
maxLen: maxLen,
}
}
func (v *UsernameValidator) Validate(ctx context.Context, username string) error {
if len(username) < v.minLen {
return errs.ErrUsernameTooShort
}
// Check uniqueness using repository
exists, err := v.userRepo.ExistsByUsername(ctx, username)
if err != nil {
return err
}
if exists {
return errs.ErrUsernameAlreadyExists
}
return nil
}当验证需要外部数据或配置时使用。
go
type UsernameValidator struct {
userRepo ports.UserRepository
minLen int
maxLen int
}
func NewUsernameValidator(
userRepo ports.UserRepository,
minLen int,
maxLen int,
) *UsernameValidator {
return &UsernameValidator{
userRepo: userRepo,
minLen: minLen,
maxLen: maxLen,
}
}
func (v *UsernameValidator) Validate(ctx context.Context, username string) error {
if len(username) < v.minLen {
return errs.ErrUsernameTooShort
}
// Check uniqueness using repository
exists, err := v.userRepo.ExistsByUsername(ctx, username)
if err != nil {
return err
}
if exists {
return errs.ErrUsernameAlreadyExists
}
return nil
}Multi-field validator
多字段验证器
Use when validation involves multiple related fields.
Port interface:
go
type RegistrationValidator interface {
ValidateEmail(email string) error
ValidatePassword(password string) error
ValidatePasswordMatch(password, confirmPassword string) error
}Implementation:
go
type RegistrationValidator struct{}
func NewRegistrationValidator() *RegistrationValidator {
return &RegistrationValidator{}
}
func (v *RegistrationValidator) ValidateEmail(email string) error {
// Email validation logic
return nil
}
func (v *RegistrationValidator) ValidatePassword(password string) error {
// Password validation logic
return nil
}
func (v *RegistrationValidator) ValidatePasswordMatch(password, confirmPassword string) error {
if password != confirmPassword {
return errs.ErrPasswordMismatch
}
return nil
}当验证涉及多个相关字段时使用。
端口接口:
go
type RegistrationValidator interface {
ValidateEmail(email string) error
ValidatePassword(password string) error
ValidatePasswordMatch(password, confirmPassword string) error
}实现:
go
type RegistrationValidator struct{}
func NewRegistrationValidator() *RegistrationValidator {
return &RegistrationValidator{}
}
func (v *RegistrationValidator) ValidateEmail(email string) error {
// Email validation logic
return nil
}
func (v *RegistrationValidator) ValidatePassword(password string) error {
// Password validation logic
return nil
}
func (v *RegistrationValidator) ValidatePasswordMatch(password, confirmPassword string) error {
if password != confirmPassword {
return errs.ErrPasswordMismatch
}
return nil
}Validation Constants
验证常量
Define validation rules as constants at the package level for clarity and maintainability.
go
const (
minPasswordLength = 8
maxPasswordLength = 128
minUsernameLength = 3
maxUsernameLength = 32
)将验证规则定义为包级常量,以提升清晰度和可维护性。
go
const (
minPasswordLength = 8
maxPasswordLength = 128
minUsernameLength = 3
maxUsernameLength = 32
)Error Handling
错误处理
Validators MUST return typed domain errors from the module's package.
When adding new custom errors, translations are mandatory in locale files.
errsgo
// In internal/modules/<module>/errs/errs.go
var (
ErrPasswordTooShort = errors.New("password must be at least 8 characters")
ErrPasswordMissingUppercase = errors.New("password must contain at least one uppercase letter")
ErrPasswordMissingLowercase = errors.New("password must contain at least one lowercase letter")
ErrPasswordMissingDigit = errors.New("password must contain at least one digit")
ErrPasswordMissingSpecial = errors.New("password must contain at least one special character")
)For every new custom error added to :
internal/modules/<module>/errs/errs.go- Add the translation key to
locales/en.json - Add the same translation key to every other existing locale file (e.g., )
locales/pt_BR.json
验证器必须返回来自模块包的类型化领域错误。添加新的自定义错误时,必须在语言环境文件中提供翻译。
errsgo
// In internal/modules/<module>/errs/errs.go
var (
ErrPasswordTooShort = errors.New("password must be at least 8 characters")
ErrPasswordMissingUppercase = errors.New("password must contain at least one uppercase letter")
ErrPasswordMissingLowercase = errors.New("password must contain at least one lowercase letter")
ErrPasswordMissingDigit = errors.New("password must contain at least one digit")
ErrPasswordMissingSpecial = errors.New("password must contain at least one special character")
)每当向添加新的自定义错误时:
internal/modules/<module>/errs/errs.go- 在中添加翻译键
locales/en.json - 在所有其他现有语言环境文件中添加相同的翻译键(例如)
locales/pt_BR.json
Context Usage
Context 使用
Validators that perform I/O operations (database lookups, API calls) MUST accept as the first parameter.
context.Contextgo
// Stateless validator - no context needed
func (v *PasswordValidator) Validate(password string) error
// Stateful validator with I/O - context required
func (v *UsernameValidator) Validate(ctx context.Context, username string) error执行I/O操作(数据库查询、API调用)的验证器必须接受作为第一个参数。
context.Contextgo
// Stateless validator - no context needed
func (v *PasswordValidator) Validate(password string) error
// Stateful validator with I/O - context required
func (v *UsernameValidator) Validate(ctx context.Context, username string) errorNaming
命名规范
- Port interface: (in
XxxValidatorpackage)ports - Implementation struct: (in
XxxValidatorpackage, same name — disambiguated by package)validator - Constructor: , returns a pointer of the struct implementation
NewXxxValidator - Validation method: for single-purpose validators, or descriptive names for multi-purpose validators
Validate
- 端口接口:(位于
XxxValidator包中)ports - 实现结构体:(位于
XxxValidator包中,名称相同——通过包名区分)validator - 构造函数:,返回结构体实现的指针
NewXxxValidator - 验证方法:单一用途验证器使用,多用途验证器使用描述性名称
Validate
Fx Wiring
Fx 配置
Add to :
internal/modules/<module>/fx.gogo
fx.Provide(
fx.Annotate(
validator.NewPasswordValidator,
fx.As(new(ports.PasswordValidator)),
),
),添加到:
internal/modules/<module>/fx.gogo
fx.Provide(
fx.Annotate(
validator.NewPasswordValidator,
fx.As(new(ports.PasswordValidator)),
),
),Dependencies
依赖关系
Validators depend on interfaces only. Common dependencies:
- — for uniqueness checks or data lookups
ports.XxxRepository - — for external validation services
ports.XxxService - Configuration values — passed as constructor parameters
验证器仅依赖于接口。常见依赖:
- — 用于唯一性检查或数据查询
ports.XxxRepository - — 用于外部验证服务
ports.XxxService - 配置值 — 作为构造函数参数传递
Testing
测试
Validators MUST have comprehensive unit tests covering:
- Valid input passes validation
- Each invalid condition returns the correct error
- Edge cases (empty strings, boundary values, special characters)
Test file location:
internal/modules/<module>/validator/<validator_name>_validator_test.gogo
package validator_test
import (
"testing"
"github.com/cristiano-pacheco/pingo/internal/modules/<module>/errs"
"github.com/cristiano-pacheco/pingo/internal/modules/<module>/validator"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPasswordValidator_ValidPassword_Passes(t *testing.T) {
// Arrange
v := validator.NewPasswordValidator()
// Act
err := v.Validate("SecureP@ssw0rd")
// Assert
require.NoError(t, err)
}
func TestPasswordValidator_TooShort_ReturnsError(t *testing.T) {
// Arrange
v := validator.NewPasswordValidator()
// Act
err := v.Validate("Ab1!")
// Assert
require.Error(t, err)
assert.ErrorIs(t, err, errs.ErrPasswordTooShort)
}验证器必须具备全面的单元测试,覆盖:
- 有效输入通过验证
- 每个无效条件返回正确的错误
- 边缘情况(空字符串、边界值、特殊字符)
测试文件位置:
internal/modules/<module>/validator/<validator_name>_validator_test.gogo
package validator_test
import (
"testing"
"github.com/cristiano-pacheco/pingo/internal/modules/<module>/errs"
"github.com/cristiano-pacheco/pingo/internal/modules/<module>/validator"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPasswordValidator_ValidPassword_Passes(t *testing.T) {
// Arrange
v := validator.NewPasswordValidator()
// Act
err := v.Validate("SecureP@ssw0rd")
// Assert
require.NoError(t, err)
}
func TestPasswordValidator_TooShort_ReturnsError(t *testing.T) {
// Arrange
v := validator.NewPasswordValidator()
// Act
err := v.Validate("Ab1!")
// Assert
require.Error(t, err)
assert.ErrorIs(t, err, errs.ErrPasswordTooShort)
}Critical Rules
关键规则
- Two files: Port interface in , implementation in
ports/validator/ - Interface in ports: Interface lives in
ports/<name>_validator.go - Interface assertion: Add below the struct
var _ ports.XxxValidator = (*XxxValidator)(nil) - Constructor: MUST return pointer
*XxxValidator - Stateless by default: Only add dependencies when validation requires external data
- Context when needed: Accept only for validators performing I/O
context.Context - Typed errors: Return domain errors from module's package
errs - Error translations: Every new custom error must have entries in and all other existing locale files
locales/en.json - Constants: Define validation rules as package-level constants
- No comments on implementations: Do not add redundant comments above methods in the implementations
- Add detailed comment on interfaces: Provide comprehensive comments on the port interfaces to describe their purpose and validation rules
- Comprehensive tests: Test valid cases and all invalid conditions
- 双文件:端口接口位于,实现位于
ports/validator/ - 接口在ports包:接口存放在
ports/<name>_validator.go - 接口断言:在结构体下方添加
var _ ports.XxxValidator = (*XxxValidator)(nil) - 构造函数:必须返回指针
*XxxValidator - 默认无状态:仅当验证需要外部数据时才添加依赖
- 按需使用Context:仅执行I/O的验证器才接受
context.Context - 类型化错误:返回模块包中的领域错误
errs - 错误翻译:每个新自定义错误必须在及所有其他现有语言环境文件中存在对应条目
locales/en.json - 常量定义:将验证规则定义为包级常量
- 实现无冗余注释:不要在实现的方法上方添加冗余注释
- 接口添加详细注释:为端口接口提供全面注释,说明其用途和验证规则 12.全面测试:测试有效场景和所有无效条件
Workflow
工作流程
- Create port interface in
ports/<name>_validator.go - Create validator implementation in
validator/<name>_validator.go - Define validation constants
- Add typed errors to module's if needed
errs/errs.go - Add translations for each new custom error in and all other existing locale files
locales/en.json - Create comprehensive unit tests in
validator/<name>_validator_test.go - Add Fx wiring to module's
fx.go - Run to verify tests pass
make test - Run to verify code quality
make lint - Run for static analysis
make nilaway
- 在中创建端口接口
ports/<name>_validator.go - 在中创建验证器实现
validator/<name>_validator.go - 定义验证常量
- 如有需要,向模块的添加类型化错误
errs/errs.go - 在及所有其他现有语言环境文件中为每个新自定义错误添加翻译
locales/en.json - 在中创建全面的单元测试
validator/<name>_validator_test.go - 向模块的添加Fx配置
fx.go - 运行验证测试通过
make test - 运行验证代码质量
make lint - 运行进行静态分析
make nilaway