Upgrade to Pro — share decks privately, control downloads, hide ads and more …

(再アップロード)Datastore/Go のデータ設計と struct の振る舞いについて

pospome
October 14, 2023

(再アップロード)Datastore/Go のデータ設計と struct の振る舞いについて

golang.tokyo #5 の登壇資料です。
https://golangtokyo.connpass.com/event/53209/

SlideshareにアップロードしたものをSpeaker Deckに再アップロードしました。

pospome

October 14, 2023
Tweet

More Decks by pospome

Other Decks in Programming

Transcript

  1. 自己紹介 twitter : pospome blog :pospomeのプログラミング日記 職種 : サーバサイドエンジニア 興味 

    : クラス設計全般, DDD アイコン:羊じゃなくてポメラニアン その他 :「ポメ」って呼んでください。
  2. ID TEL Email Profile Image Profile Movie HP ATK DEF

    1 000 x x.png 100 100 100 2 111 y y.mp4 50 50 50 3 222 z z.png 200 200 200
  3. ID TEL Email Profile Image Profile Movie HP ATK DEF

    1 000 x x.png 100 100 100 2 111 y y.mp4 50 50 50 3 222 z z.png 200 200 200
  4. type User struct { //他のフィールドは省略 ProfileImage, ProfileMovie string } それぞれが単なるフィールドだと

    「Image, Movie どちらか一方を登録する」 という仕様を表現するのは難しい
  5. 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 自体に持たせることで 仕様を表現することができる
  6. ID TEL Email Profile. Image Profile. Movie HP ATK DEF

    1 000 x x.png 100 100 100 2 111 y y.mp4 50 50 50 3 222 z z.png 200 200 200
  7. ID TEL Email Profile. Image Profile. Movie HP ATK DEF

    1 000 x x.png 100 100 100 2 111 y y.mp4 50 50 50 3 222 z z.png 200 200 200
  8. 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 を表現している
  9. 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 = “” のように抽象度がマッチしない場合 に比べると変更に強くなる
  10. ID Contact .TEL Contact .Email Profile. Image Profile. Movie HP

    ATK DEF 1 000 x x.png 100 100 100 2 111 y y.mp4 50 50 50 3 222 z z.png 200 200 200
  11. ID Contact .TEL Contact .Email Profile. Image Profile. Movie HP

    ATK DEF 1 000 x x.png 100 100 100 2 111 y y.mp4 50 50 50 3 222 z z.png 200 200 200
  12. 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 に依存しているが、 他の値には依存していない
  13. 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 を汚さずに「対戦」を表現できる
  14. ID Contact .TEL Contact .Email Profile. Image Profile. Movie Battle.

    HP Battle.A TK Battle. DEF 1 000 x x.png 100 100 100 2 111 y y.mp4 50 50 50 3 222 z z.png 200 200 200
  15. 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 は以下になる
  16. ID Contact .TEL Contact .Email Profile. Image Profile. Movie Battle.

    HP Battle.A TK Battle. DEF 1 000 x x.png 100 100 100 2 111 y y.mp4 50 50 50 3 222 z z.png 200 200 200 User struct を保存する User Kind は以下になるので、 最初のデータ構造とは違うものになった