Bootstrap

[ Golang 中的 DDD 实践] 值对象

译者:baiyutang

原文:

让我们开始 Golang 中最重要的模式领域驱动设计之旅:值对象。

说某些模式是最重要的可能有些夸张,但我不会对此争论。我第一次听说是来自 。那时,它看起来十分简单并没太多意思。第二次我阅读到来自 。那时,模式开始言之有理,越来越多的并且很快我无法想象不使用值对象写任何代码。

简单而美好

值对象是看起来简单的模式。它把属性分为简单的单元以提供特定的行为。单元体现出我们能够在现实世界发现并绑定到复杂对象的特定质量或数量。它提供一些特定值或特征。他可以是颜色或金钱(值对象的),电话号码、或其他提供一些值的小对象,像下面的代码块:

type Money struct {
  Value    float64
  Currency Currency
}

func (m Money) ToHTML() string {
  returs fmt.Sprintf(`%.2f%s`, m.Value, m.Currency.HTML)
}

type Salutation string

func (s Salutation) IsPerson() bool {
  returs s != "company" 
}  

type Color struct {
  Red   byte
  Green byte
  Blue  byte
}

func (c Color) ToCSS() string {
  return fmt.Sprintf(`rgb(%d, %d, %d)`, c.Red, c.Green, c.Blue)
}

type Address struct {
  Street   string
  Number   int
  Suffix   string
  Postcode int
}

type Phone struct {
  CountryPrefix string
  AreaCode      string
  Number        string
}

在 Golang 中,值对象可以用结构体或通过扩展原始类型来表示。在这两种情况下,想法是为了单一或一组值提供附加行为。在多数情况下,值对象能提供字符的特定方法格式化,以定义值对象在 JSON 加解密的行为。尽管如此,这些方法的主要目的应该是支持现实生活中的特征或数量绑定到业务不变量上。

标志与相等

值对象没有标志,这是它与实体模式()关键的区别。实体模式具有标志用以描述唯一性。如果两个实体某种同一性,意味着我们在讨论相同的对象。值对象没有标志,值对象只有一些字段更好的描述其值,为了测试两个对象相等,我们需要检测所有的字段相等,像下面的代码:

// checking equality for value objects
func (c Color) EqualTo(other Color) bool {
  return c.Red == other.Red && c.Green == other.Green && c.Blue == other.Blue
}

// checking equality for value objects
func (m Money) EqualTo(other Money) bool {
  return m.Value == other.Value && m.Currency.EqualTo(other.Currency)
}

// checking equality for entities
func (c Currency) EqualTo(other Currency) bool {
  return c.ID.String() == other.ID.String()
}

在上面的例子, 和 结构体都定义了 方法用于检查他们所有的字段。另一方面,标志相等的通用性检查在本例中使用了 UUID。

type Coin struct {
  Value Money
  Color Color
}

type Colors []Color

// value object on web service
type Currency struct {
  Code string
  HTML int
}

// entity on payment service
type Currency struct {
  ID   uuid.UUID
  Code string
  HTML int
}

明确

type Birthday time.Time

func (b Birthday) IsYoungerThen(other time.Time) bool {
  return time.Time(b).After(other)
}

func (b Birthday) IsAdult() bool {
  return time.Time(b).AddDate(18, 0, 0).Before(time.Now())
}

const (
  Freelancer = iota
  Partnership
  LLC
  Corporation
)

type LegalForm int

func (s LegalForm) IsIndividual() bool {
  return s == Freelancer
}

func (s LegalForm) HasLimitedResponsability() bool {
  return s == LLC || s == Corporation