35
Datastore/Go のデータ設計と struct の振る舞いについて

Datastore/Go のデータ設計と struct の振る舞いについて

  • Upload
    pospome

  • View
    4.239

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Datastore/Go のデータ設計と struct の振る舞いについて

Datastore/Go のデータ設計とstruct の振る舞いについて

Page 2: Datastore/Go のデータ設計と struct の振る舞いについて

自己紹介twitter : pospomeblog :pospomeのプログラミング日記職種 : サーバサイドエンジニア興味  : クラス設計全般, DDDアイコン:羊じゃなくてポメラニアンその他 :「ポメ」って呼んでください。

Page 3: Datastore/Go のデータ設計と struct の振る舞いについて

今日の発表の結論を言うとDatastoreのデータ構造の設計やレビューでは

Kindに持たせる値だけではなく、値が持つ振る舞いと特性も一緒に考えた方がいい

Page 4: Datastore/Go のデータ設計と struct の振る舞いについて

ソーシャルゲームのユーザー情報を表現する User Kind を例に説明します

Page 5: Datastore/Go のデータ設計と struct の振る舞いについて

ID TEL Email ProfileImage

ProfileMovie HP ATK DEF

1 000 x x.png 100 100 1002 111 y y.mp4 50 50 503 222 z z.png 200 200 200

Page 6: Datastore/Go のデータ設計と struct の振る舞いについて

RDBで考えるとこの構造で問題ないかもしれないがDatastore で適切とは限らない

Datastore のデータ構造をマッピングした struct の振る舞いを通して考えてみる

Page 7: Datastore/Go のデータ設計と struct の振る舞いについて

例1ProfileImage と ProfileMovie はどちらか一方しか登録できない

という仕様を表現する

Page 8: Datastore/Go のデータ設計と struct の振る舞いについて

ID TEL Email ProfileImage

ProfileMovie HP ATK DEF

1 000 x x.png 100 100 1002 111 y y.mp4 50 50 503 222 z z.png 200 200 200

Page 9: Datastore/Go のデータ設計と struct の振る舞いについて

type User struct {//他のフィールドは省略

ProfileImage, ProfileMovie string}

それぞれが単なるフィールドだと「Image, Movie どちらか一方を登録する」

という仕様を表現するのは難しい

Page 10: Datastore/Go のデータ設計と struct の振る舞いについて

これを修正すると・・・

Page 11: Datastore/Go のデータ設計と struct の振る舞いについて

type User struct {Profile Profile

}type Profile struct {

Image, Movie string}func NewImageProfile(image string) Profile {

return Profile{Image: image,

}}func NewMovieProfile(movie string) Profile {

return Profile{Movie: movie,

}}

「Image, Movie どちらか一方を登録する」というルールを Profile 自体に持たせることで

仕様を表現することができる

Page 12: Datastore/Go のデータ設計と struct の振る舞いについて

ID TEL Email Profile.Image

Profile.Movie HP ATK DEF

1 000 x x.png 100 100 1002 111 y y.mp4 50 50 503 222 z z.png 200 200 200

Page 13: Datastore/Go のデータ設計と struct の振る舞いについて

例2Email, TEL は

OAuth Scope = Contact でしか取得できないという仕様を表現する

Page 14: Datastore/Go のデータ設計と struct の振る舞いについて

ID TEL Email Profile.Image

Profile.Movie HP ATK DEF

1 000 x x.png 100 100 1002 111 y y.mp4 50 50 503 222 z z.png 200 200 200

Page 15: Datastore/Go のデータ設計と struct の振る舞いについて

type User struct {TEL, Email string

}func Get(scope string) User {

var u User = GetUserFromDB()if scope != "Contact" {

u.TEL = ""u.Email = ""

}return u

}

ロジック上で Contact scope = Email, TEL を表現している

Page 16: Datastore/Go のデータ設計と struct の振る舞いについて

これを修正すると・・・

Page 17: Datastore/Go のデータ設計と struct の振る舞いについて

type User struct {Contact Contact

}type Contact struct {

TEL, Email string}func Get(scope string) User {

var u User = GetUserFromDB()if scope != "Contact" {

u.Contact = nil}return u

}

Scope が扱う Contact という概念は具体的に TEL, Email を含む

抽象度の違う値同士を扱おうとすると、ロジックが複雑になる可能性があるTEL, Email という具体的な概念を

Scope の Contact という抽象度に合わせるScope と struct が一致しているので、

直感的に理解しやすい

Contact の持つ値が変化しても、u.Contact = nil に修正は発生しない

これはロジックが Contact という抽象度の概念を扱っているからであって、

t.TEL = “” のように抽象度がマッチしない場合に比べると変更に強くなる

Page 18: Datastore/Go のデータ設計と struct の振る舞いについて

ID Contact.TEL

Contact.Email

Profile.Image

Profile.Movie HP ATK DEF

1 000 x x.png 100 100 1002 111 y y.mp4 50 50 503 222 z z.png 200 200 200

Page 19: Datastore/Go のデータ設計と struct の振る舞いについて

例3ATK, DEF は HP の値によって増減する

という仕様を表現する

Page 20: Datastore/Go のデータ設計と struct の振る舞いについて

ID Contact.TEL

Contact.Email

Profile.Image

Profile.Movie HP ATK DEF

1 000 x x.png 100 100 1002 111 y y.mp4 50 50 503 222 z z.png 200 200 200

Page 21: Datastore/Go のデータ設計と struct の振る舞いについて

type User struct {HP, ATK, DEF int

}func (u *User) GetAtk() int {

//HPに依存するreturn u.ATK * u.HP

}func (u *User) GetDef() int {

if u.HP < 100 {//ピンチになると強くなるreturn u.DEF * 2

}return u.DEF

}

それぞれの値を算出するロジックはHP, ATK, DEF に依存しているが、

他の値には依存していない

Page 22: Datastore/Go のデータ設計と struct の振る舞いについて

これを修正すると・・・

Page 23: Datastore/Go のデータ設計と struct の振る舞いについて

type User struct {Battle Battle

}type Battle struct {

HP, ATK, DEF int}func (b *Battle) GetAtk() int {

//HPに依存するreturn b.ATK * b.HP

}func (b *Battle) GetDef() int {

if b.HP < 100 {//ピンチになると強くなるreturn b.DEF * 2

}return b.DEF

}

HP, ATK, DEF を Battle として定義「対戦」に関するロジックは

Battle に集中させるUser は「対戦」以外のロジックに集中できる

対戦のロジックはゲームのコアな要素なので、複雑な仕様になりやすいUser から分離しておくと

Battle に interface を持たせて特定のロジックを抽象化させたり、

固定値を設定した Battle に差し替えるなど、User を汚さずに「対戦」を表現できる

Page 24: Datastore/Go のデータ設計と struct の振る舞いについて

ID Contact.TEL

Contact.Email

Profile.Image

Profile.Movie

Battle.HP

Battle.ATK

Battle.DEF

1 000 x x.png 100 100 1002 111 y y.mp4 50 50 503 222 z z.png 200 200 200

Page 25: Datastore/Go のデータ設計と struct の振る舞いについて

ということで、最終的に・・・

Page 26: Datastore/Go のデータ設計と struct の振る舞いについて

type User struct { ID int64 Contact Contact Profile Profile Battle Battle}type Contact struct { TEL, Email string}type Profile struct { Image, Movie string}type Battle struct { HP, ATK, DEF int}

振る舞いを考慮すると User struct は以下になる

Page 27: Datastore/Go のデータ設計と struct の振る舞いについて

ID Contact.TEL

Contact.Email

Profile.Image

Profile.Movie

Battle.HP

Battle.ATK

Battle.DEF

1 000 x x.png 100 100 1002 111 y y.mp4 50 50 503 222 z z.png 200 200 200

User struct を保存する User Kind は以下になるので、最初のデータ構造とは違うものになった

Page 28: Datastore/Go のデータ設計と struct の振る舞いについて

まとめ

Page 29: Datastore/Go のデータ設計と struct の振る舞いについて

Datastoreのデータ構造の設計やレビューではKindに持たせる値だけではなく、

値が持つ振る舞いと特性も一緒に考えた方がいい

Page 30: Datastore/Go のデータ設計と struct の振る舞いについて

RDBだと struct の構造をそのまま保存するものではないので

必要に応じてORMや手動マッピングロジックで永続化データとモデルをマッピングする

struct の振る舞いを考慮してテーブル構造を考える必要性は低い

永続化データとしての正しさを考えればいいRDBはSQLによる柔軟なクエリが可能なので、無理やり struct を保存する工夫をするよりも永続化データとしての正しさを重視した方がいい

Page 31: Datastore/Go のデータ設計と struct の振る舞いについて

Datastore は struct をそのまま保存できるので、データ設計の段階で struct を考慮して設計すると

手戻りが少なく、自分でインピーダンスミスマッチを解消する必要もない

極端に言うとDatastore設計 = モデル設計

ただし、Datastore に保存できないデータ構造もあるので注意

Page 32: Datastore/Go のデータ設計と struct の振る舞いについて

Kind のプロパティ名に Prefix がある場合はその値は関連性の高い値である可能性が高い

関連性の高い値はそれ独自の振る舞いや特性を持つ可能性が高い

そういった関連性の高いデータに対して仕様を表現するロジックを紐付けることによって、

責務が明確になる

Page 33: Datastore/Go のデータ設計と struct の振る舞いについて

今回の例は説明用ということもあって、結構無理矢理なケースかと思います

今回の例であればDatastore のプロパティを

フラットに並べても問題ないかもしれませんここはモデルの設計方針によって変わります

重要なのは「struct の振る舞いも考慮する」

という選択肢を持つことです

Page 34: Datastore/Go のデータ設計と struct の振る舞いについて

Datastore の設計をする際にはstruct の振る舞いも考慮してみてはいかがでしょうか?

Page 35: Datastore/Go のデータ設計と struct の振る舞いについて

おわり