ここ3年位、Laravel Rocketという名前のLaravelのコードジェネレータというか、ボイラープレーティングの一連のライブラリ/ツールを作っています。これまで特にどこかで紹介するチャンスもなかったのですが、どういうものかをここでまとめておきたいと思います。
https://laravel-rocket.github.io/#
Laravel Rocket 概要
Laravel Rocketは、Laravelのアプリケーションをなるべく早く、しかも誰が書いてもそこそこ同じような整理されたコードになるように作りたい、という気持ちで生まれたものです。
MySQL Workbenchを使ってデータベース設計を作り、(必要なら)APIの設計をOpenAPI(Swagger)で作成すると、マイグレーションファイル、Model、Repository、そこそこ使えるCRUD(Reactベース)、APIを自動生成してくれます。テストも非常に基礎的なものであれば、作ってくれます。Facebook Loginとか、Adminの権限管理とかも用意してあります。テーブルをあとから変更したり、Serviceレイヤなどを追加する事もできます。なので、このツールを使うと、データベースの設計とAPIの仕様を決めれば、あとはその間をつなぐビジネスロジックを書くだけで、基本的なAPIサーバが割と早く作れるようになっています。設計だけ作ってコードを自動生成して、後はフロントエンドを構築すればよいだけになる、というのが全体の思想です。
そしてもう一つの目的は、「誰が書いてもそこそこ同じようなコードになる」ということです。そもそもWeb Application Frameworkの目的がそもそも、コードを早く、そして誰が書いてもだいたい同じようなコードになる、ということが目的だとは思いますが、Laravelは、比較的自由度が高いフレームワークですし、書き方は開発者によってばらばらになりがちです。そのため、チーム内で、新しいプロジェクトを開始するときに、担当する開発者に書き方が依存してしまうと、あとで他のメンバーが参画するときなどに読みづらい、またあるいはそもそもジュニアな開発者が開発を開始するときに設計でミスを犯しやすい、という問題が発生しがちです。それを解決するために、ルールをたくさん決めてもよいのですが、コードジェネレータを使い、そもそも基礎のコードを自動生成することで、ベースとなるコードの部分では少なくともそうした差異をなくそうとしていることも、開発のきっかけです。
Laravel Rocket を作ったきっかけ
自分は、ソフトウエアを作る仕事をしています。特に、スタートアップの超初期や企業の新規事業チームに参画して、MVPの設計、構築をしたりするなどの仕事が多く、速い速度で開発をすることに価値がありました。しかも、だいたいそういう場合の基本的な構造は、ログインがあり、管理画面があり、何らかのデータを出し入れして、画面にいい感じに表示する、という意味では、あまり変わらないので、毎回同じようなコードを書くのは無駄が多いな、と思いました。
また、4年前からはNexus Frontier Techという会社をやっており、現在この会社はAI/ML Opsに特化していますが、初期はベトナムに開発チームを持ち、やはりスタートアップの超初期の支援や、新規事業のチームをサポートする仕事を受けていていました。特にベトナムの場合は、早くサービスを立ち上げるだけでなく、スキルの高くない開発者でもそこそこちゃんとしたコードを書けるようにすることが大きな課題で、一人ひとりに手取り足取り思想やスキルを身に着けさせるのは、転職が盛んで、若者が多いベトナムでは時間もかかるので、成果物のクオリティの担保が大変です。そこで、コードを自動生成するのがやはり良い、という結論になりました。
Laravel Rocketの基本的な使い方
Laravel Rocketの大きな問題点は、ドキュメントが殆どない点で、これは自分でもわかっているのですが、自分で使うために作り始めたツールで、どうしても後回しになってしまっています。
プロジェクトの作成
Laravel Rocketを使う際には、まず一般的なLaravelのプロジェクト作成方法を使う代わりに、Laravel Rocketのコマンドラインツールを使ってプロジェクトを作成します。なのでまずはそれをインストールします。
composer global require "laravel-rocket/installer"
すると、rocket
というコマンドが使えるようになるはずなので、それを使って、新しいプロジェクトを作成します。
rocket new [awesome project name]
これで、自動的にプロジェクトが作成されます。自動的にcomposer update
がされ、各種ライブラリもインストールされます。
データベースの設計
続いて、データベースの設計を行います。 documents/db.mwb
というファイルがあるので、これをMySQL Workbenchというツールで開きます。このツールは、テーブルの設計をGUIで行うことができるツールで、Relationなども設定できます。
名前の通りでMySQLのツールなので、PostgreSQLの場合はどうするのか?という話もあるのですが、自分がほぼMySQLを選択している事情もあり、このツールを使うようになっています。このツールで設計をして、PostgreSQLで運用することも可能ですが、色々調整が必要かもしれません。
コードの自動生成
設計してファイルを保存したら、このdocuments/db.mwb
から、マイグレーションファイル、Model、Repository、Admin CRUD、あとテストファイルを一気に作ります。そのためのartisanコマンドを用意しています。
php artisan rocket:generate:from-mwb
問題がなければ、これで基本的なファイル群がすべて生成されます。その後必要な場合は.env
ファイルを修正して、php artisan migrate
および php artisan db:seed
でデータベースを用意した後、php artisan serve
でサーバを起動できます。
http://localhost:8000/admin
で管理画面にアクセスすれば、CoreUIを使った管理画面が自動生成されているのがわかります。
app.json
による微調整
Laravel Rocketではコードの自動生成時にdocuments/app.json
というファイルを参照して、特にCRUDの調整が色々できます。例えば選択をチェックボックスで行うか、セレクタにするか、編集をどの権限で可能にするか、などです。ただこれはまだ色々調整中で、Buggyなので、今修正を行っているところです。
APIの設計
API(JSON over HTTP、一般にRESTと言われる形式)のAPIを、Swagger(OpenAPI Spec)ファイルから自動生成することができます。その際にはdocuments/api_v1.yml
などの名前でAPIの定義ファイルを作成し、以下のように実行します。
php artisan rocket:generate:api:from-oas --osa=./documents/api_v1.yml
これで、ルーティングの設定、Controllerや、その他APIに必要なファイルが生成されます。
ビジネスロジック
自動生成が終わったら、ここからはビジネスロジックを書いていくことで、アプリケーションを完成させるわけですけれど、Laravel Rocketの世界観では、ビジネスロジックはControllerやModelではなくて、Serviceというレイヤに書くことにしているので、app/Services
にサービスを定義する。そのためのGeneratorも用意してあります。
php artisan rocket:make:service Message
こうすると、MessageService
というファイルが生成されるので、そこにメソッドを追加し、ビジネスロジックを記述していきます。
また、Controllerだけでなく、どこからでも呼び出すような汎用的なメソッドはHelperに記述することにしているので、Helperも自動生成できるようにしてあります。
php artisan rocket:make:helper Message
ServiceやHelperも、生成時にテストファイルを生成してくれますが、現状だとインスタンスが生成できるのかのテストしかしていない(メソッドがまだ存在していないので当然ですが)ので、何らかの形で、後で追加したメソッドのテストも追加できるようにしたいです。
テーブル構造などの変更
生成後にテーブルの構造などを変更したくなった場合、documents/db.mwb
を修正して、再度以下のように生成を実行すると、差分が更新され、Alter Tableが行われるMigrationファイルが生成され、Model、Repository等もアップデートされます。
php artisan rocket:generate:from-mwb
ただし、Model、RepositoryなどPHPのクラスのアップデートについて、生成後のマニュアル修正されたものに関してはできる限り残すようにしていますが、処理や判定ロジックが甘いので、修正後に確認が必要です。ここはもう少し今後うまく処理できるようにしたいです。
Admin CRUDの修正
Admin CRUDは、Reactで実装してあり、必要なAPIとReactのファイル群も自動で生成されます。しかしカスタマイズが必要になることも多い(カスタマイズが必要なことは、なるべく今後少なくしていきたいと思っています)。その場合は、resources/assets/admin
のファイルを修正し、 yarn dev
などでビルドをやり直してください。
Laravel Rocketで生成されるコードの構造
ここで、いくつかLaravel Rocketのコードの構造を紹介しておきます。一般的なものばかりなので、特に目新しさはないですし、アーキテクチャ設計をしたのはちょっと前で、まだまだ改善の余地はあると思います。
Repositoryパターン
Laravelでは非常に一般的になっているいわゆるLaravelにおけるRepositoryパターンです。ここなどに説明が書かれていますが、Modelの生成や更新、取得などをここに集約させています。
追加した特徴として、動的にメソッドを追加しており、findByMessageId
など、特定のカラム名を指定してデータ取得を行うことができることや、allByFilter
など、配列として指定したフィルタでの取得が可能で、フィルタがbuildQueryByFilter
というメソッドで定義されているので、この関数をオーバーライドすることで自由にフィルタを定義することができるので、あまり多くの関数を定義しなくても、モデルへのアクセスができるようになっている点などがあります。
Decoratorパターン
ModelからView用のロジックを分離するパターンで、app\Presenters
にそれぞれのモデルごとにPresenterクラスが用意されています(自動生成されます)。Presenterを実現するライブラリはいくつかあったのですが、開発初期にメンテされていなかったり、微妙なコードだったりしてなかなか良いものがなかったため、自作しています。
$model->present()->name
のように、present
というメソッドを挟むことで、Presenterにアクセスできます。Presenterにもしメソッドが追加されておらず、Modelにその名前のデータが存在した場合は、自動的にそちらが出力されます(例えば上記の例では、name
というメソッドが定義されていなかった場合は、$model
のname
プロパティが呼び出される。
Response オブジェクト
Laravelでは、Requestはオブジェクトとして分離され、その中でValidationなどを行うことができますが、Reponseもオブジェクトとして定義するようにして、その中でModelのデータの変換などを行うようにしてあります。 app\Http\Responses
に各クラスが置かれています。
Laravel Rocketの内部処理や使っているライブラリなどについて
そんなにすごいテクニックは使っていないのですが、例えば既存のコードに新たに行を追加するに当たり、正規表現で無理やりやるのはなかなか限界があるため、nikic/PHP-Parserを使って一応きちんと解析しています。MySQL Workbenchのパーサは、takaaki-mizuno/mwb-parserというものを自分で書きました。実際は、JSONなりなんなりにExportしてから処理すればよいのかもしれませんが、自分はこうしたファイル解析が好きで、むかしからこういうものは自作してしまう傾向があるので、趣味的なものと言えます。また、MySQL Workbenchで修正して、すぐ反映できるほうが便利かと思ったので。
Swaggerのファイルの解析も、以前はあまり手に馴染むものがなかったために現在は自作のライブラリでやっていますが、あまりできが良くないですし、ここを頑張るのは違うと思うので、今後既存のライブラリに切り替えていきたいなと考えています。
Laravel Rocketの今後
Laravel Rocketは自分や自分のチームのメンバーがいろいろなプロジェクトをこなす中で、少しずつ進化してきたツールです。しかも、私が考える「読みやすい設計」を実現しているにすぎず、「自分は設計思想が異なる」という人もたくさんいるのではないかと思いますし、進化の中で色々バグや不完全なものを抱えたままの部分もあります。なので、今後も少しずつ進化させつつ、もう少しドキュメントを書いていこうかなと思っています。
特に手を入れたい部分としては、以下の点があります。
- Admin CRUDのより賢い生成
- テーブルやAPIの更新時のより賢いアップデート
- ファイルのアップロードがS3しか対応していないので、クラウドベンダに依存しない設計にする
それから、これはLaravel向けのツールですが、結局の所DB設計とAPI設計は他の言語、他のWAFでもそんなに変わらないわけで、同じデータベース/API設計から、Railsも、Djangoも、Ginも行ければ更に良いなと思っておりまして、DB設計とAPI設計を中間形態のファイルに変換した後、各種言語/WAFに変換する、というようなことができれば最高だなと思っています。そのために、MySQL WorkbenchのパーサをGoで再実装したりしてみましたが、まだ先には進められていません。
もし、Laravel Rocketを試して見られたりした方がいたら、あるいはなにかご指摘などしてくださる方が居れば、幸いです。