SlideShare uma empresa Scribd logo
1 de 35
Baixar para ler offline
Datastore/Go のデータ設計と
struct の振る舞いについて
自己紹介
twitter : pospome
blog :pospomeのプログラミング日記
職種 : サーバサイドエンジニア
興味  : クラス設計全般, DDD
アイコン:羊じゃなくてポメラニアン
その他 :「ポメ」って呼んでください。
今日の発表の結論を言うと
Datastoreのデータ構造の設計やレビューでは
Kindに持たせる値だけではなく、
値が持つ振る舞いと特性も一緒に考えた方がいい
ソーシャルゲームのユーザー情報を表現する User Kind を
例に説明します
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
RDBで考えるとこの構造で問題ないかもしれないが
Datastore で適切とは限らない
Datastore のデータ構造をマッピングした struct の
振る舞いを通して考えてみる
例1
ProfileImage と ProfileMovie は
どちらか一方しか登録できない
という仕様を表現する
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
type User struct {
//他のフィールドは省略
ProfileImage, ProfileMovie string
}
それぞれが単なるフィールドだと
「Image, Movie どちらか一方を登録する」
という仕様を表現するのは難しい
これを修正すると・・・
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 自体に持たせることで
仕様を表現することができる
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
例2
Email, TEL は
OAuth Scope = Contact でしか取得できない
という仕様を表現する
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
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 を表現している
これを修正すると・・・
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 = “” のように抽象度がマッチしない場合
に比べると変更に強くなる
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
例3
ATK, DEF は HP の値によって増減する
という仕様を表現する
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
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 に依存しているが、
他の値には依存していない
これを修正すると・・・
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 を汚さずに「対戦」を表現できる
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
ということで、最終的に・・・
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 は以下になる
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 は以下になるので、
最初のデータ構造とは違うものになった
まとめ
Datastoreのデータ構造の設計やレビューでは
Kindに持たせる値だけではなく、
値が持つ振る舞いと特性も一緒に考えた方がいい
RDBだと struct の構造を
そのまま保存するものではないので
必要に応じてORMや手動マッピングロジックで
永続化データとモデルをマッピングする
struct の振る舞いを考慮してテーブル構造を
考える必要性は低い
永続化データとしての正しさを考えればいい
RDBはSQLによる柔軟なクエリが可能なので、
無理やり struct を保存する工夫をするよりも
永続化データとしての正しさを重視した方がいい
Datastore は struct をそのまま保存できるので、
データ設計の段階で struct を考慮して設計すると
手戻りが少なく、
自分でインピーダンスミスマッチを解消する必要もない
極端に言うと
Datastore設計 = モデル設計
ただし、
Datastore に保存できないデータ構造もあるので注意
Kind のプロパティ名に Prefix がある場合は
その値は関連性の高い値である可能性が高い
関連性の高い値は
それ独自の振る舞いや特性を持つ可能性が高い
そういった関連性の高いデータに対して
仕様を表現するロジックを紐付けることによって、
責務が明確になる
今回の例は説明用ということもあって、
結構無理矢理なケースかと思います
今回の例であれば
Datastore のプロパティを
フラットに並べても問題ないかもしれません
ここはモデルの設計方針によって変わります
重要なのは
「struct の振る舞いも考慮する」
という選択肢を持つことです
Datastore の設計をする際には
struct の振る舞いも考慮してみてはいかがでしょうか?
おわり

Mais conteúdo relacionado

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

第1回 Jubatusハンズオン
第1回 Jubatusハンズオン第1回 Jubatusハンズオン
第1回 Jubatusハンズオン
JubatusOfficial
 
第1回 Jubatusハンズオン
第1回 Jubatusハンズオン第1回 Jubatusハンズオン
第1回 Jubatusハンズオン
Yuya Unno
 
Phpではじめるオブジェクト指向(公開用)
Phpではじめるオブジェクト指向(公開用)Phpではじめるオブジェクト指向(公開用)
Phpではじめるオブジェクト指向(公開用)
VOYAGE GROUP
 

Semelhante a Datastore/Go のデータ設計と struct の振る舞いについて (14)

[AI08] 深層学習フレームワーク Chainer × Microsoft で広がる応用
[AI08] 深層学習フレームワーク Chainer × Microsoft で広がる応用[AI08] 深層学習フレームワーク Chainer × Microsoft で広がる応用
[AI08] 深層学習フレームワーク Chainer × Microsoft で広がる応用
 
Apexコアデベロッパーセミナー070726 配布用
Apexコアデベロッパーセミナー070726 配布用Apexコアデベロッパーセミナー070726 配布用
Apexコアデベロッパーセミナー070726 配布用
 
DTrace for biginners part(2)
DTrace for biginners part(2)DTrace for biginners part(2)
DTrace for biginners part(2)
 
第1回 Jubatusハンズオン
第1回 Jubatusハンズオン第1回 Jubatusハンズオン
第1回 Jubatusハンズオン
 
第1回 Jubatusハンズオン
第1回 Jubatusハンズオン第1回 Jubatusハンズオン
第1回 Jubatusハンズオン
 
The overview of Server-ide Bulk Loader
 The overview of Server-ide Bulk Loader The overview of Server-ide Bulk Loader
The overview of Server-ide Bulk Loader
 
Phpではじめるオブジェクト指向(公開用)
Phpではじめるオブジェクト指向(公開用)Phpではじめるオブジェクト指向(公開用)
Phpではじめるオブジェクト指向(公開用)
 
Postgre sql9.3新機能紹介
Postgre sql9.3新機能紹介Postgre sql9.3新機能紹介
Postgre sql9.3新機能紹介
 
DB TechShowcase Tokyo - Intelligent Data Platform
DB TechShowcase Tokyo - Intelligent Data PlatformDB TechShowcase Tokyo - Intelligent Data Platform
DB TechShowcase Tokyo - Intelligent Data Platform
 
「ビジネス活用事例で学ぶ データサイエンス入門」輪読会#5資料
「ビジネス活用事例で学ぶ データサイエンス入門」輪読会#5資料「ビジネス活用事例で学ぶ データサイエンス入門」輪読会#5資料
「ビジネス活用事例で学ぶ データサイエンス入門」輪読会#5資料
 
For Power BI Beginners
For Power BI BeginnersFor Power BI Beginners
For Power BI Beginners
 
ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装
 
Tokyo r 25_lt_isobe
Tokyo r 25_lt_isobeTokyo r 25_lt_isobe
Tokyo r 25_lt_isobe
 
Oracle Cloud Developers Meetup@東京
Oracle Cloud Developers Meetup@東京Oracle Cloud Developers Meetup@東京
Oracle Cloud Developers Meetup@東京
 

Mais de pospome (10)

トランザクションスクリプトのすすめ
トランザクションスクリプトのすすめトランザクションスクリプトのすすめ
トランザクションスクリプトのすすめ
 
MicroServices & APIs
MicroServices & APIsMicroServices & APIs
MicroServices & APIs
 
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考えるGoのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
 
どこに何を書くのか?
どこに何を書くのか?どこに何を書くのか?
どこに何を書くのか?
 
アプリケーションコードにおける技術的負債について考える
アプリケーションコードにおける技術的負債について考えるアプリケーションコードにおける技術的負債について考える
アプリケーションコードにおける技術的負債について考える
 
Goのシンプルさについて
GoのシンプルさについてGoのシンプルさについて
Goのシンプルさについて
 
パッケージの循環参照
パッケージの循環参照パッケージの循環参照
パッケージの循環参照
 
Controllerのbefore_actionにおける インスタンス変数セットについて
Controllerのbefore_actionにおける インスタンス変数セットについてControllerのbefore_actionにおける インスタンス変数セットについて
Controllerのbefore_actionにおける インスタンス変数セットについて
 
REST API のコツ
REST API のコツREST API のコツ
REST API のコツ
 
サーバサイドNodeの使い道
サーバサイドNodeの使い道サーバサイドNodeの使い道
サーバサイドNodeの使い道
 

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