Is this the best way in Go to create an Entity that can't be set into an invalid state?
Here's an example of how I might implement it. I'm unsure if this is very idiomatic.
package custom_subdomain
import (
"errors"
"regexp"
)
type domainReg struct {
domainName string
// Other values ...
}
func NewDomainRegistration(domainName string) (*domainReg, error) {
reg := &domainReg{}
if err := reg.SetDomainName(domainName); err != nil {
return nil, err
}
return reg, nil
}
func (r *domainReg) GetDomainName() string {
return r.domainName
}
func (r *domainReg) SetDomainName(domain string) error {
matches, err := regexp.Match("\\.example.com$", []byte(domain))
if err != nil {
return err
}
if !matches {
return errors.New("all domains must be sub-domains to .example.com")
}
r.domainName = domain
return nil
}
type DomainRegistration interface {
GetDomainName() string
SetDomainName(domain string) error
}
package main
import (
"fmt"
"github.com/purplebooth/entity-state-example"
"log"
)
func main() {
reg, err := custom_subdomain.NewDomainRegistration("test.example.com")
if err != nil {
log.Panic(err)
}
fmt.Println(reg.GetDomainName())
}
Top comments (3)
Would I be correct in understanding that you want to avoid having a
domainReg
instance with uninitialized values? If so, yes, I believe this is a valid implementation. The vast majority of the packages that I've seen or used have some variety of yourcustom_subdomain.NewDomainRegistration()
function. They allow you to control how things get initialized, which is not possible with plain-old struct instance construction.Please correct me, if I'm misreading. :)
Awesome! Yeah that's exactly what I want to do.
For some reason it didn't feel very "go"
Sweet! I felt the same initially. It's almost like one of those "when in Rome" situations. I will say, it's quite nice when you decide you want to mock internal functions in unit tests. Then you only have to build upon the initialization function, like any other new feature, rather than having to adopt it much later.