あんさんぶるスターズ！！２タイトル同時運営を支えるマスタデータの仕組み

Happy Elements Advent Calendar 2020 14日目の記事です。

# あんさんぶるスターズ！！２タイトル同時運営について
　2015年に弊社からリリースされた「あんさんぶるスターズ！」は今年の3月、「あんさんぶるスターズ！！」として旧あんさんぶるスターズ！をアップデートした「あんさんぶるスターズ！！Basic」と、音楽ゲームでアイドルたちの3Dライブを楽しめる新規アプリ「あんさんぶるスターズ！！Music」の２タイトル同時運営という形態になりました。

　この２タイトルは、同じUIを使用している部分があったり、スカウトやイベント、キャンペーン施策の多くは共通で開催されます。本記事では、この２タイトル運営におけるマスタデータの運用方法について紹介します。色々な会社でマスタデータの管理方法は工夫されていると思いますが具体的に公開されている事例は少ないので、今回記事にしてみたら面白いかなと思い至りました。

　なお、本記事で掲載しているデータは説明用の仮データであり、実際のデータとは異なります。

# マスタデータの仕組み
　あんさんぶるスターズ！！のマスタデータは全てGoogle SpreadSheetで管理されています。BasicとMusicで分かれているシートもありますが、可能な限り同じシートのデータを扱うことで、２つのタイトルでデータの差異によるミスが無くなるようにしています。
　それらのSpreadSheetは基本的に非エンジニア職のプランナーの方などがデータの入力を行います。入力が楽になるところは関数で勝手に入るように設定して効率化を図っています。

　SpreadSheetを利用している理由としては、Excel式のデータ入力は多くの人に馴染みがあり、それ以上便利な他ツールの選定や独自のマスタデータツールを作るのはコストが高すぎると感じたのと、あとはExcelをGitなどでバージョン管理するパターンもあると思いますが、SpreadSheetなら共同編集の利便性が高く、編集が手軽な点などで採用しています。

## マスタデータの出力
　SpreadSheetに入力されたデータをエンジニアの手によってサーバーに取り込む必要があります。ここはcsvで書き出すなど色々なやり方があると思いますが、あんさんぶるスターズ！！ではGoogle Apps Script(GAS)を用いてRuby on Railsにマイグレーションするためのコードを出力できるようにしています。

　マイグレーションコードを出力する際に、シート内の列名とRailsのテーブルのカラムを関連付けるための段階があります。以下のようにSpreadSheetの別シートに定義してそれをGASで読み込むようにしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123340/104aa4b4-0e6a-7b08-d0f8-c0131291d380.png)
　これは、「カード」というシートから「Card」というRailsのModelを生成するための定義です。1行目にシートの列名を列挙して、2行目に出力で使用したいRails上のカラム名を入力します。3行目はその列をどの型で出力するかを定義しています。

　また、4行目に書いているように「basic」「music」と記載しておくことでどちらかのタイトルでしか使用しない列として扱うようにしています。同じデータでもBasicとMusicで値が違う場合があるので、それぞれの列を用意して出力されることもあります。ゲームの性質の違い上どちらかのタイトルでしか使用しない列に対しても同様です。

　これをもとに以下のデータをMusic用に出力すると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123340/2e55f594-ed0c-82eb-ab6f-5a8fc9162653.png)


```
Card.create(id: 1, character_name: "明星スバル", name: "ひとりのアイドル", type: FLASH, mv_costume: "共通アイドル衣装") 
```

という出力になり、これをRailsに取り込みます。データの更新をしたい場合は

```
Card.find(1).update(...
```
という形式で書き出すこともできるようにしています。
　また、出力データはSpreadSheet上のカーソルで選択中の行を出力するようにしてあり、必要な行だけ複数選択して一気に出力もできるようにしています。

　このあたりの手入力データ→サーバーデータの変換はアプリ運営では色々なやり方をしていると思います。そもそもサーバーデータを手入力するケースもあると思いますが、あんさんぶるスターズ！！では非エンジニアのプランナーが直感的に入力しやすく、かつエンジニアの手間も減らせるように自動化された仕組みになっています。

# 恒常施策におけるマスタデータの入力・出力の仕組み
　あんさんぶるスターズ！！では毎月、２回のイベント開催と４回のスカウト追加を行っています。さらにアイドルの誕生日が月平均４回程度あり、各アイドルごとの誕生日キャンペーンも実施しています。これらの恒常的な施策をいかに効率良く運用していけるかはアプリ運営において重要な部分だと考えており、効率が良くなればなるほど余剰な工数を作りだして新規施策などアプリとしてさらに展開していきたいことに注力することができます。

　余剰な工数を作り出すためにはただ人員を増やせばいいというわけではないことがほとんどで、逆にコミュニケーションコストが増えたり、伝達ミスなども増えてしまうパターンが多いと思います。
　あくまで一例としてですが、あんさんぶるスターズ！！で運営を行う中で現状最適化されている仕組みを紹介します。

## 恒常施策におけるマスタデータの入力
　恒常施策においては施策ごとに専用のスプレッドシートがあり、関連するデータを一つのシートにまとめています。一つのシートにまとまっていることで関連するデータがわかりやすく、誰でもそのシートを見ればそのイベントやスカウトの内容を把握することができます。

　基本的にはプランナーがこのシートに必要項目を一定のルールで入力し、GASでそれぞれ該当するマスタデータのSpreadSheetに出力していきます。
　例えば、イベントに関して以下のようなシートがあるとします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123340/fa49d046-6c17-079c-82ff-8559136f89d7.png)
　各項目ごとに、
-  項目の表題（イベント）
- カラム名（ID, タイトル, ...）
- データ（複数可）

というルールで入力するようにしています。

　これを、それぞれ実際のマスタデータのSpreadSheetへGASで出力できるようにしています。どの項目からどのマスタSpreadSheetへどのような形式で出力するかは全イベントシート共通で別のシートで定義しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123340/bbed5702-6730-40b3-36d7-d15dbf52c864.png)
　説明用に少し形式を変えていますが、おおよそ上記のようなルールでマスタデータとの関係性を定義しています。

　上記の定義では、イベントの項目からイベントとイベントリボン交換所のマスタへ出力されるようになっていて、マスタデータのSpreadSheetのスキーマを「スキーマ」の行に入れています。これを「参照」の行で指定したイベントシートのカラムをもとに、「値」の行で整形してマスタデータのSpreadSheetへ出力します。

　このマスタデータのSpreadSheetへの同期はSpreadSheetのメニューバーに独自の項目を追加して実行できるようにしてあり、ここまでデータ入力担当のプランナーさんにやってもらっています。

## 恒常施策におけるマスタデータの出力
　マスタデータの出力は、前述のマイグレーションコードの出力手法を用いて、施策ごとの専用のスプレッドシートから関連するマスタデータを全て出力できるようにしています。例えば前述のようなイベントシートに対しては、これも全イベントシート共通で別のシートで出力するマスタデータのSpreadSheetを定義しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123340/379576c0-a75c-7783-a66c-18c6693a91cc.png)

　この定義を元にRuby on Railsのマイグレーションのコードが出力されます。これもSpreadSheetの拡張でメニューバーから実行できるようにしています。

　これらの仕組みから最終的にMusicでは以下のように特定の施策に関連する全てのデータに関するマイグレーションコードを作成することができます。

```
Event.create!([
  {id: 1, title: "新星団★輝きのBIGBANG！", began_at: "2020/03/31 15:00", ended_at: "2020/04/08 22:00"},
])

# 略

Card.create!([
  {id: 1, character_name: "明星 スバル", name: "新星の舞台", type: FLASH},
  {id: 2, character_name: "衣更 真緒", name: "新星の立場", type: GRITTER},
  {id: 1, character_name: "氷鷹 北斗", name: "新星の士気", type: BRILLIANT},
  {id: 1, character_name: "遊木 真", name: "新星の立案", type: SPARKLE},
])

MvCostume.create!([
  {id: 1, name: "BIGBANG衣装", character_name: "明星 スバル"},
  {id: 2, name: "BIGBANG衣装", character_name: "衣更 真緒"},
  {id: 3, name: "BIGBANG衣装", character_name: "氷鷹 北斗"},
  {id: 4, name: "BIGBANG衣装", character_name: "遊木 真"},
])
```

# 実際に運用を行ってみての所感
　この仕組みが整う前は、プランナーが施策シートに入力した後にそれぞれのマスタデータのSpreadSheetにデータ入力をしており、二度手間が発生していました。この過程は、施策シートを更新したのにマスタデータの方の更新が漏れていたなどのミスを招く可能性のある過程でもありました。

　現状、この仕組みによってデータ作成の作業時間は短縮できており、プランナーの入力データミス以外の人為的ミスはかなり防げている気がします。データ入力のミスはエンジニア側でデータチェックツールを作るなどで対策するしかないかなと思っています。

　プランナーとしては直感的なデータの入力と、各施策が一枚のシートにまとまっているという仕様把握がしやすい状態にできており、エンジニアとしても施策シートに入力されたデータに関連するデータを一括で出力できるようになっており快適です。Google Apps Script様々です。

　BasicとMusicの2タイトルを同じマスタデータを参照して運用するあんさんぶるスターズ！！としては効率的な仕組みになっていると感じています。

　ただ、これら一連の仕組みのメンテナンスコストがあり、マスタデータのSpreadSheetを追加するときなどはこの仕組みを把握している人がそれぞれ項目を追加している状態です。また、カラム数の多いシートや施策に関連するシート数が増えてきたときに、施策シートとマスタシートを関連付ける定義のシートが長くなってわかりづらくなってきており、そのあたりは改善点だと感じています。

# メンバー募集
Happy Elements株式会社 カカリアスタジオでは、
いっしょに【熱狂的に愛されるコンテンツ】をつくっていただけるメンバーを大募集中です！
もし弊社にご興味持っていただけましたら、是非一度
下記採用サイトをご覧ください。
[Happy Elements株式会社 採用特設サイト](https://recruit.happyelements.co.jp/)