条件式評価器の実装による
管理ツールの抽象化
2017/03/25(土)
@Go Conference 2017 Spring
The Go gopher was designed by Renee French.
The gopher stickers was made by Takuya Ueda.
Licensed under the Creative Commons 3.0
Attributions license.
自己紹介
メルカリ/ソウゾウ
上田拓也
twitter: @tenntenn
■ コミュニティ活動
Google Cloud Platform User Group (GCPUG) Tokyo
Goビギナーズ
golang.tokyo
Go Conference
■ 業務
GAE/Goでメルカリアッテを作ってます
GoやGCPコミュニティを盛り上げる仕事
Gopherを描く仕事(LINEスタンプ)
2
アッテ開発の技術 : Golang と Google App Engine
アジェンダ
■ 管理ツールの抽象化
● 管理ツールとは?
● 管理ツールを抽象化するとは?
● バナーツールにおける抽象化
● デモ
■ 抽象化の実現
● 条件式評価器
● UIの自動生成
■ まとめ
4
管理ツールの抽象化
5
管理ツール
6
■ 各社呼び名が違う
● CSツール/バナーツール
● CS/オペツール
● CMS
● OPEツール
● 管理画面
■ 要するに何をするツールか?
● サービスのコンテンツなどを管理
● ユーザデータなどを閲覧
管理ツールを抽象化するとは?
■ 「抽象化」 = 汎用的に使えるようにする
● 複数のプロジェクトで使える
● 複数の用途で使える
■ 抽象化することのメリットは?
● 開発/運用の工数が少なくて済む
● 使い方を覚える必要がない
■ 抽象化は難易度が高い
● 何を汎用的にすればいいのか?
● 使いづらいツールにならないか?
● 設計ミスで余計に工数がかかる
7
具体例から抽象化を考える −バナーツール
■ バナーツール
● モバイルアプリにバナーを配信する
● バナーの管理や配信条件を設定する
8
デモ
9
YouTubeで見る
抽象化できるポイント1
■ コアな機能を抽象化(汎用化)する
● 配信条件を式で書けるようにする
○ String(os) == "ios" など
● 設定できるレスポンスを自由にする
10
アプリ バナーツール
バナー取得
OS, APIバージョン などの変数
条件に当てはまるバナー
画像URL, 遷移先URL
条件式とレスポンス
を設定する
抽象化できるポイント2
■ UIを抽象化する
● データからUIを自動生成する
○ 条件式を構築するUIを保存された条件から作る
○ レスポンスの設定を行うUIを対象サービス(アプリ)
ごとにスキーマを設定し、自動生成する
11
String(os) == Enum(__os, "0,1", "iOS,Android")
os: iOS ▼
Android
UIを生成する部分リクエストから
もらう部分
条件式から作る例
String(os) == "1"
UIで選んだ値で展開※一部簡略化している
抽象化の実現
12
条件式評価器の実装
13
■ goパッケージを用いて式の評価器を作る
● go/parserパッケージで式のASTを取得
● ASTを再帰的に評価していく
■ 定数評価器をうまく使う
● go/constantパッケージを使う
● constant.Valueに変換する
==
"A" "B"
BinaryExpr
BasicLit
"A" == "B" false
パース 定数評価
参考:goパッケージで簡単に静的解析して世界を広げよう
定数評価器の利用
■ go/constantパッケージ
● 定数同士の演算ができる
14
func BinaryOp(x Value,op token.Token,y Value) Value
例:二項演算
constant.Valueに変換できれば、
簡単に演算をやってくれる
型付き変数の実装
■ 式の中で型を表す
● 関数呼び出しを変数として扱う
● 大文字で始まる関数呼び出しのみ
● 引数は識別子のみでそれを変数名とする
■ 変数を評価する
● 式の評価時には値が決まっている
● 評価器から見ると名前付きの定数に近い
● constant.Valueにして評価
15
Int(n) == 10
型 変数名
関数の実現
■ 関数を使えるようにする
● 小文字から始まる関数呼び出しを対象
● 引数は任意の式
● オリジナルの組込み関数のみ使用できる
○ cond, until, hours, days,...
■ 関数呼び出しを評価する
● const.Valueを引数と戻り値に取る関数
● reflect.MakeFuncの思想と似ている
● 関数呼び出しごとにスコープを作る
○ 別の式を呼び出すcond関数のため
16
関数の例
■ 引数と戻り値をconstant.Valueにする
17
func add(args []constant.Value) []constant.Value {
x, _ := strconv.ParseInt(args[0].String(),10,64)
y, _ := strconv.ParseInt(args[1].String(),10,64)
return []constant.Value{
constant.MakeInt64(x + y),
}
}
※スペースの都合上、エラー処理は省いてある
type Func interface {
Do(args []constant.Value) []constant.Value
}
例:足し算
条件式を保存して呼び出す
■ cond関数
● よく使う式をDBに保存しておける
● cond関数を使って呼び出せる
● 呼び出し時に変数に値を束縛できる
18
String(os) == Enum(__os, "0,1", "iOS,Android")
保存された式 (id:1)
cond(1,"__os=1")
呼び出す式
列挙型の変数
変数の値
呼び出す式のID
String(os) == "1"
展開した式
JSON SchemaとUIの自動生成
■ JSON Editor
● JSON SchemaからWeb UIを作る
● 保存されたデータからJSON Schemaを自動
生成してやればよい
19
※画像はJSON Editorのリポジトリより
条件式からUIを自動生成する
■ 保存された式を組み合わせる
● __で始まる変数をUIで設定する
● UIで設定した変数を束縛する形でcond関数
を用い、バナーの条件式とする
20
String(os) == Enum(__os, "0,1", "iOS,Android")
os: iOS ▼
Android
保存された式 (id:1)
cond(1,"__os=1")JSON Schema
バナーに設定される式
作業者は保存された条件を
ANDやORで組み合わせるだけで済む
UIが生成される 選んだ値を設定する
その他の機能
■ バリデーション
● 型情報を用いたバリデーション
○ 型情報からJSON Schemaが作れる
● ゼロ値を用いたバリデーション
○ 式が真偽値になるかどうか判定できる
■ あいまい検索
● 変数を一部だけ与えて式を評価できる
● condをすべて展開し、使われていない変数
を含むASTのノードを枝刈りしていく
21
抽象化した恩恵
■ バナー以外の管理でも使える
● 期間/優先度/条件を持つ情報を管理でき
る
● ゲームのマスタ管理
○ イベント、ショップ など
● 運営からのお知らせ
22
1プロジェクトで4箇所くらい
使おうとしている例も!
まとめ
■ 管理ツールの抽象化
● コア機能を汎用的に作る
● UIを自動生成する
● 特定のサービスに依存するものはデータとし
て持つ
■ 抽象化の実現
● goパッケージを使う
● JSON Schemaを使ってUIを自動生成
23
golang.tokyo ☓ GCPUG
24
4月27日(木)開催予定@メルカリ
LT発表者募集中!
https://golangtokyo.connpass.com/event/53209/
Thank you!
twitter: @tenntenn
Qiita: tenntenn
connpass: tenntenn
25
26
補足
型をバリデーションに利用する
■ バリデーションを行う
● JSON Schemaを使えばJSONがスキーマを
満たしているかチェックできる
● 変数に型を導入することで、型情報を基にス
キーマを定義できる
● 型に沿ったバリデーションができる
■ オリジナル型を作る
● 組込み型としてオリジナルの型を作れる
○ URL, IntRange など
● バリデーションも行える
27
ゼロ値をバリデーションに利用する
■ 式を評価すると真偽値になるか判定する
● 条件式中に変数があると評価できない
● 変数にはゼロ値がある
● 変数にゼロ値を設定してみて評価する
○ 評価結果が真偽値になればOK
28
Int(n) == 10 0 == 10 false
Int(n) + 10 0 + 10 10
例1
例2
ASTの操作とあいまい検索
■ 条件式を検索する
● 変数の一部を与えて検索する
● condを展開する必要がある
● 与えられなかった変数を使っている部分を無
視して評価する
29
&&
== ==
v1 1 v22
v1が与えられた場合
&&
== true
v1 1
&&か||がくるまで
上にのぼる
&&ならtrue
||ならfalseに置き換える
テスト配信条件に条件式を応用する
■ 汎用的に作って再利用する
● QA用に特定のユーザにだけ配信したい
● アプリ毎に条件が違う
○ ユーザID、IP など
● テスト配信にも条件式評価器がそのままつ
かえる
● UIもスキーマから生成できる
30
テスト配信状態になっていて、
テスト配信条件に当てはまる
リクエストのみテスト用バナーが配信される
抽象化できるポイント3
■ マルチテナント型にする
● UIはデータから自動生成される
○ サービスごとに適したUIが作られる
● データベースをサービスごとに分ける
● エントリポイントによってデータベースを切り
替える
31
簡単に複数サービス(アプリ)
に対応できる!
マルチテナント型にする
■ 1ソースで複数のアプリに提供する
● Google App Engine のNamesace APIを使
う
● DBの名前空間をアプリごとに分ける
● ホスト名ごとに名前空間を作る
32
詳しくは
「Namespace API を用いたマルチテナント型 Web アプリの実践」
という資料で!

条件式評価器の実装による管理ツールの抽象化