LoginSignup
4
1

More than 3 years have passed since last update.

Goで値オブジェクトを表現する

Last updated at Posted at 2020-06-15

はじめに

最近、ドメイン駆動開発設計入門を読み進めているんですが、サンプルコードがC#で書かれているんですね。
個人的にはC#を業務で書いているので、そのまま読み進めればコードは理解できるんですが、どうせなら違う言語で書いてみたいな〜と思い、最近興味をもっているGo言語で書いてみました!!!

実装

main.go
package main

import (
    "fmt"

    "github.com/AwataKyosuke/qiita/domain/model/user"
)

func main() {

    // 無効なユーザーの作成
    _, err1 := user.New(0, "sample")
    if err1 != nil {
        fmt.Println(err1)
    }

    // 無効なユーザーの作成
    _, err2 := user.New(1, "")
    if err2 != nil {
        fmt.Println(err2)
    }

    // 有効なユーザーの作成
    user1, err := user.New(1, "sample")
    if err != nil {
        fmt.Println(err)
    }

    // 有効なユーザーの作成
    user2, err := user.New(1, "sample")
    if err != nil {
        fmt.Println(err)
    }

    // ユーザー情報の表示
    fmt.Println(user1.GetID())
    fmt.Println(user1.GetName())
    fmt.Println(user1.Equals(*user2))
}

user.go
package user

// 先頭を小文字にすることで外部パッケージから参照できないようにする
type user struct {
    id   id
    name name
}

// ユーザーを作成する場合は、この関数を呼ぶ
// 先頭が大文字なので、外部パッケージから呼び出すことができる
// この関数以外ではユーザーを作成できないようにする
func New(id int, name string) (*user, error) {

    createdID, err := newID(id)
    if err != nil {
        return nil, err
    }

    createdName, err := newName(name)
    if err != nil {
        return nil, err
    }

    user := user{
        id:   *createdID,
        name: *createdName,
    }
    return &user, nil
}

// getterだけを定義することで、外部からの変更を許可しない
func (u user) GetID() id {
    return u.id
}

// getterだけを定義することで、外部からの変更を許可しない
func (u user) GetName() name {
    return u.name
}

func (u user) Equals(target user) bool {
    // 等価チェックで判断する
    return (u.id == target.id && u.name == target.name)
}

id.go
package user

import "errors"

// id型を定義
type id int

// idを作成する場合は、この関数を呼ぶ
func newID(value int) (*id, error) {

    // ここにルールを指定することで、不正なデータを作成できないようにする
    if value < 1 {
        return nil, errors.New("idは1以上の整数である必要があります。")
    }
    id := id(value)
    return &id, nil
}
name.go
package user

import "errors"

// name型を定義
type name string

// nameを作成する場合は、この関数を呼ぶ
func newName(value string) (*name, error) {

    // ここにルールを指定することで、不正なデータを作成できないようにする
    if len(value) < 1 {
        return nil, errors.New("nameは1文字以上である必要があります。")
    }
    name := name(value)
    return &name, nil
}
実行結果
$ go run main.go 
  idは1以上の整数である必要があります。
  nameは1文字以上である必要があります。
  1
  sample
  true

終わりに

こうすれば、ユーザーを作成したい場合は、user.New(1, "sample")のように、Newを呼ぶ必要があるので、値オブジェクトと同じような振る舞いをするんじゃないかと思って作りました。
DDDもGoも初心者なので、間違っていれば指摘をもらえると嬉しいです!
ちなみに「ドメイン駆動設計入門」ですが、既に買ってよかった感が凄いので、おすすめです!

...これであってるんだろうか

参考文献

4
1
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1