こんにちは、コロプラのサーバーエンジニアの山田です。
コロプラでは、多数のプロジェクトでゲームサーバーに Laravel を使用しています。そして、 社内用に Laravel を拡張したフレームワークの laravel-extension はコードの自動生成機能を備えており、これによって開発リソースの節約に大きく貢献しています。今回はその自動生成について活用方法や効果をご紹介します。
laravel-extension とサーバー基盤
Laravel はその汎用性と拡張性により多くの開発者に選ばれていますが、コロプラでは、コロプラにおけるゲームのサーバー開発に特化し、独自の拡張を行った laravel-extension を開発しています。
laravel-extension はDB (Cloud Spanner, TiDB, MySQL), Redis, BigQuery などの外部コンポーネントに対するカスタムドライバーや、サーバー/クライアントコードの自動生成、その他多数の機能を提供しています。これらの機能によりプロジェクトはゲーム仕様側の設計・開発に専念できます。
基盤で開発している内製ライブラリの一部はOSSとして公開もしており、laravel-extension とは切り離されているので通常の Laravel プロジェクトに導入できます。
今年9月に開催した勉強会『ゲームバックエンド開発を加速させる!サーバー共通基盤の全貌』にて laravel-extension などを開発しているサーバー基盤に関して弊社のエンジニアが話しているので、ご覧いただけると嬉しいです!
コロプラのサーバーにおける自動生成
コロプラではサーバーサイドのコード生成において nette/php-generator を活用しています。
nette/php-generator はシンプルな記述でPHPのコードが自動生成できます。
例として公式のサンプルを引用すると、以下のようなコードを書いた場合
<?php $class = new Nette\PhpGenerator\ClassType('Demo'); $class ->setFinal() ->setExtends(ParentClass::class) ->addImplement(Countable::class) ->addComment("Description of class.\nSecond line\n") ->addComment('@property-read Nette\Forms\Form $form'); // to generate PHP code simply cast to string or use echo: echo $class;
このように出力できます。
<?php /** * Description of class. * Second line * * @property-read Nette\Forms\Form $form */ final class Demo extends ParentClass implements Countable { }
今回は詳しく触れませんが、最新の PHP 8.1 の Enum などに対応していたり、PSR-12 / PER に準拠したコードが出力できたりなど、PHP のコード自動生成における基本的な機能が揃っています。
そして、元となる定義ファイルに関しては、laravel-extension 独自の構造を持つ YAML で定義しています。
構造としては OpenAPI Spec を想像していただけると良いのですが、OpenAPI Spec よりシンプルでコロプラのゲームサーバー要件に特化した形なので、学習コストが低く記述量が少ないものとなっています。
以下はあくまで例ですが、Controller や route を生成するAPI定義の場合は以下のようになります。
name: Item description: アイテム actions: list: description: アイテムのリスト method: POST request: params: type: ItemType # Enum という別の定義のものを参照 response: params: items: type: Item # Struct という別の定義のものを参照 array: true # PHP 上で Laravel の Collection にキャストされたり、クライント上では配列として扱える
標準で生成できるコードとしては様々あり、一つの定義ファイルから複数の種類のファイルを出力しています。
定義元 |
生成されるコード |
Controller.yml |
Controller |
Struct.yml |
Struct … プレゼンテーション層の構造体 |
Enum.yml |
Enum … 基本はクラスベースの独自仕様の Enum だが、PHP8.1のネイティブなEnumにも定義次第で変更可能 |
Model.yml |
Model (Eloquent) |
他にも redis, BigQuery などを扱うコードもあり、一部は定義ファイルが YAMLではなくconfig の php を流用しているものも存在します。
なお、クライアントコード(C#)に関しては Laravel の blade テンプレートを使用して生成し、クライアントに生成ファイルを共有しています。
自動生成で一部工夫されている点としては、自動生成されるファイルは「一度だけ生成されるもの」と「生成ごとに上書きされるもの」に分けられることです。例えば Model の場合「HogeModel」は一度だけ生成され、「HogeModelAbstract」は上書きされる形式で、Abstract にカラム、リレーションの定義・操作などを入れることでYAML定義の変更に伴う手動修正が最小限に抑えられます。
また、自動生成は簡単に拡張できるようになっており、プロジェクト側で拡張している一例だと、Google Sheets からデータを読み取りエラーコードやメッセージの自動生成などしています。私の周りでは、人間が手動で行うべきではないコードは自動生成に委ねていこうという文化が根付いているように感じます。もちろん効率化の具合や保守の容易さなどを考慮して必要性を判断しています。
少し別の物として、コロプラのリアルタイム通信基盤である prizm もこの laravel-extension 用のYAML定義に基づいた Go のコードの自動生成を行っています。詳しくは『【Go Tech Talk】スケーラビリティのための3社合同LT』にて説明しているので興味がある方は以下の資料をご覧ください。
このように、抽象化された定義によって様々なプログラミング言語やプラットフォームに対応する柔軟で迅速な開発が実現されています。
自動生成による効果
私自身が感じている自動生成の効果をいくつかあげます。
当然なのですが、Controller, Model, 構造体など、記述量が多いコードの大半が自動生成になることにより開発者はより本質的な開発に集中できます。「APIを追加/変更したい」となったときに、Controller, route, テストコード, クライアントコードなどの調整が必要になりますが、これが YAML を書き換えるだけでほとんどやってくれるので、単純な API であればさくっと作れます。
また、人為的なミスも減らせます。一つの定義ファイルから複数種類のコードが生成可能なことによって、人為的なミスの介入箇所をYAMLに集約させることができます。また単純にそれぞれの環境でコード記述量も減るので、バグのリスク低減と開発速度向上に繋がっています。特に Eloquent のカラム/インデックス/リレーションなどの同じ文字列を打つようなコードだとタイポなどのミスが起きうると思いますが、そこを生成時にエラーで気付けたりするのでより安全で低コストです。
そして、私が個人的に一番大きいと思っているのはクライアントコードの生成です。API や構造体が追加/変更になった際にクライアントエンジニアに共有するのですが、自動生成されたクライアントコードを共有して使い方や注意事項などを説明するだけで済むので、クライアント側とのコミュニケーションコストが大きく減少しているのを感じます。API 追加の度にパラメータの構造や型などを文字でまとめたり説明するのは想像するだけで億劫ですね。
さいごに
自動生成を駆使することで、私たちは日々の開発業務において単調で繰り返しの多い作業から解放され、より創造的かつ効率的な作業に集中できるようになりました。
今回は nette/php-generator に関する詳細な説明を省略しましたが、今後の記事でこのツールに焦点を当てた詳細な解説を行う予定です。
ColoplTechについて
コロプラでは、勉強会やブログを通じてエンジニアの方々に役立つ技術や取り組みを幅広く発信していきます。
connpassおよびX(Twitter)で情報発信していますので、是非メンバー登録とフォローをよろしくお願いいたします。
また、コロプラではゲームや基盤開発のバックエンド・インフラエンジニアを積極採用中です!
興味を持っていただいた方はぜひお気軽にご連絡ください。