https://go.dev/play/p/B7ErJjt8JqU
modelの役割ですが、
① entity,usecase,repo,querierなど繋ぐ
② validationなどの業務ロジックの担保
②の配慮でmodelのfiledは非公開で、Setter都度Validationする、常に完璧な状態を求めて、
trade-offで使いにくく、①の役割を損する。
なので、①の役割をPrimativeなstruct(一旦voと呼ぶ)に分担する方いいかなと思う。
vo<=>modelの変換は手間ですが、複雑ではないので、ある程度自動作成できる。
-- go.mod --
module sample
go 1.19
-- model/name.go --
package model
import "errors"
type Name struct {
    string
}
func NewName(v string) (Name, error) {
    n := Name{v}
    if err := n.validate(); err != nil {
        return Name{}, err
    }
    return n, nil
}
func (n Name) validate() error {
    if len(n.string) > 10 {
        return errors.New("name length must be less than equal 10")
    }
    return nil
}
func (n Name) String() string {
    return n.string
}
-- model/user.go --
package model
import "errors"
// entity
type User struct {
    id   string
    name Name
    age  *int
}
func (u User) validate() error {
    if u.id == "" {
        return errors.New("id must not be empty")
    }
    if err := u.name.validate(); err != nil {
        return err
    }
    return nil
}
func NewUser(vo UserVO) (User, error) {
    return vo.ToModel()
}
func InitUser(vo UserVO) (User, error) {
    vo.ID = "1234567890" // random
    return vo.ToModel()
}
func (u User) ToVO() UserVO {
    return UserVO{
        ID:   u.id,
        Name: u.name.String(),
        Age:  u.age,
    }
}
type UserVO struct {
    ID   string
    Name string
    Age  *int
}
func (uvo UserVO) ToModel() (User, error) {
    u := User{
        id:   uvo.ID,
        name: Name{uvo.Name},
        age:  uvo.Age,
    }
    if err := u.validate(); err != nil {
        return User{}, err
    }
    return u, nil
}
-- main.go --
package main
import (
    "fmt"
    "sample/model"
)
func main() {
    fmt.Println("success pattern")
    age := 12
    //New
    vo := model.UserVO{
        ID:   "s11600",
        Name: "kume",
        Age:  &age}
    u, err := model.NewUser(vo)
    if err != nil {
        fmt.Println("err:", err)
    }
    fmt.Printf("valid user, %+v\n", u)
    //Init
    vo = model.UserVO{
        Name: "ku",
        Age:  &age}
    u, err = model.InitUser(vo)
    if err != nil {
        fmt.Println("err:", err)
    }
    fmt.Printf("valid user, %+v\n", u)
    //update
    vo = u.ToVO()
    vo.Name = "updated"
    vo.Age = nil
    u, err = vo.ToModel()
    if err != nil {
        fmt.Println("err:", err)
    }
    fmt.Printf("update success, %+v\n", u)
    fmt.Println("--------------")
    fmt.Println("failed pattern")
    vo.Name = "invalid name"
    u, err = vo.ToModel()
    if err != nil {
        fmt.Println("err:", err)
    }
    fmt.Printf("update failed, %+v\n", u)
}