SlideShare a Scribd company logo
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 の振る舞いも考慮してみてはいかがでしょうか?
おわり

More Related Content

PDF
MP Joinを使った類似データ抽出
PDF
Template Meta Programming入門から応用まで
PDF
Van laarhoven lens
PDF
Analysis of Learning from Positive and Unlabeled Data
PDF
居場所を隠すために差分プライバシーを使おう
PDF
初心者講習会資料(Osaka.R#7)
PDF
差分プライバシーによる時系列データの扱い方
PDF
闇魔術を触ってみた
MP Joinを使った類似データ抽出
Template Meta Programming入門から応用まで
Van laarhoven lens
Analysis of Learning from Positive and Unlabeled Data
居場所を隠すために差分プライバシーを使おう
初心者講習会資料(Osaka.R#7)
差分プライバシーによる時系列データの扱い方
闇魔術を触ってみた

Similar to Datastore/Go のデータ設計と struct の振る舞いについて (19)

ODP
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
PDF
Swift 3.0 の新機能 - 追加・変更まわりだけ、ざっくり紹介 2 #devsap
PDF
Swift らしい表現を目指そう #eventdots
PDF
Introduction to cocoa sql mapper
PDF
Swift 3 を書くときに知っておきたい API デザインガイドライン #love_swift #akibaswift
PPTX
日記アプリのデータ管理
PDF
SwiftにおけるClassとStructの使い分け
PDF
20141128 iOSチーム勉強会 My Sweet Swift
PDF
第2回 モデリング勉強会
PDF
すごいH 第12章モノイド
PDF
モバイルゲームの「大規模な開発」かつ「高頻度の更新」を実現するための開発環境整備の取り組み
PDF
「バグあるある」と「仕様変更あるある」一挙大放出SP!
KEY
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
PDF
NS Prefix - そこから見渡す Swift 3 の景色 #startup_mobile
PDF
詳解Dexファイルフォーマット
PDF
Rubyを使ったオブジェクト指向デザイン実践:第一章発表
KEY
1.29.user,user,user
PPTX
レガシーな Perl システムに DDD (ドメイン駆動設計)を取り入れる
PDF
20120831 mongoid
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Swift 3.0 の新機能 - 追加・変更まわりだけ、ざっくり紹介 2 #devsap
Swift らしい表現を目指そう #eventdots
Introduction to cocoa sql mapper
Swift 3 を書くときに知っておきたい API デザインガイドライン #love_swift #akibaswift
日記アプリのデータ管理
SwiftにおけるClassとStructの使い分け
20141128 iOSチーム勉強会 My Sweet Swift
第2回 モデリング勉強会
すごいH 第12章モノイド
モバイルゲームの「大規模な開発」かつ「高頻度の更新」を実現するための開発環境整備の取り組み
「バグあるある」と「仕様変更あるある」一挙大放出SP!
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
NS Prefix - そこから見渡す Swift 3 の景色 #startup_mobile
詳解Dexファイルフォーマット
Rubyを使ったオブジェクト指向デザイン実践:第一章発表
1.29.user,user,user
レガシーな Perl システムに DDD (ドメイン駆動設計)を取り入れる
20120831 mongoid
Ad

More from pospome (9)

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

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