Goで理解するDDD – エンティティ


ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本」書籍がDDDを実践しはじめるのに分かりやすくとてもよいのですが、忘れてしまい何度か読み直しているのでまとめておきます。

エンティティとは

エンティティとは、ドメインモデルを実装したドメインオブジェクトです。値オブジェクトも同様にドメインモデルを実装したドメインオブジェクトですが、両者の違いは同一性によって識別されるか否かです。

同一性によって識別されるとはつまりシステムにおいては、ID(identity)があるかと考えると分かりやすいかもしれません。

エンティティの性質

  • 可変である
  • 同じ属性であっても区別される
  • 同一性によって区別される

可変である

値オブジェクトは不変なオブジェクトですが、エンティティは可変なオブジェクトです。値オブジェクトとは異なり、ChangeName で名前の変更が可能です。

type User struct {
    id   string
    name string
}

func NewUser(name string) (*User, error) {
    u := &User{
        id: uuid.NewString(),
    }
    err := u.ChangeName(name)
    if err != nil {
        return nil, err
    }
    return u, nil
}

func (m *User) ChangeName(name string) error {
    if name == "" {
        return fmt.Errorf("ユーザ名は必須です。")
    }
    if len(name) < 3 {
        return fmt.Errorf("ユーザ名は3文字以上です。%s", name)
    }
    m.name = name
    return nil
}

同じ属性であっても区別される、同一性によって区別される

システムのユーザ名を変更しても ID が変わらないように、属性が同じでもユーザAとユーザBが区別されるように ID(同一性)によって区別されるのがエンティティです。同じ名前のユーザを作成しても userA と userB は区別されます。

func (u *UserUsecase) Compare() (bool, error) {
    userA, err := model.NewUser("taro")
    if err != nil {
        return false, err
    }

    userB, err := model.NewUser("taro")
    if err != nil {
        return false, err
    }
    // false となる
    return userA == userB, nil
}

エンティティの判断基準

値オブジェクトとエンティティはどちらもドメインの概念を表現するオブジェクトとして似通っていますが、エンティティにするかの基準は、ライフサイクルが存在しそこに連続性が存在するかが大きな基準となります。

値オブジェクトかエンティティかはシステムによって異なる

車にとってのタイヤとタイヤ工場にとってのタイヤの例えがわかりやすいです。

たとえば車にとってタイヤはパーツです。特性に細かい違いはあるものの交換可能でまさに値オブジェクトとして表現可能な概念です。しかし、タイヤを製造する工場にとってはどうでしょうか。タイヤにはロットがあり、それがいつ作られたものであるかという個体を識別することは重要なことです。タイヤはエンティティとして表現する方が相応しいでしょう。

まとめ

  • コードのドキュメント性が高まる
  • ドメインにおける変更がコードに伝わりやすい

エンティティについて簡単にまとめましたが、詳しくは下記書籍にてご確認ください。

その他参考書籍


関連記事