Protocol Bufferの定義(スキーマ)ファイルを独立させる
今回からしばらくFlutterのスマホアプリとサーバーサイドのJavaとの間でgRPCでやりとりするための、Protocol Bufferのスキーマ定義の運用に関して記述していきたいと思います。
gRPCの良いところは、
- ペイロードが小さい
- proto(スキーマ定義)ファイルの可読性・厳格性が高い
- サーバーサイドから複数のレスポンスを返せたり、サーバーサイドからプッシュ型の通信ができるなど、うまく使えば、ユーザーに新しい体験をもたらすことができる
などです。
特に、効率の良いHTTP2をもとにしたバイナリベースのプロトコルであるため、通信量を削減できることは、スマホアプリとしてはありがたく、積極的に利用しない手はないのではないでしょう。
前置きが長くなりました。
さて、現在でも一般的に多く利用されているRESTfulなJavaのサーバーとJSONでやりとりするモデルの場合、SwaggerなどのOpen-APIパーサーを用いて、バックエンドの担当者がフロント側にスキーマを出力してあげるという運用では多いのではないでしょうか。
ですが、この運用だと、型の変更やエンドポイントの変更に伴ってバックエンド側が主体となって変更作業をするケースが多く、作業が完了するまではフロント側の作業をブロックしてしまいがちです。
また、フロント側がこの作業を担う場合は、別途Javaの開発環境を整える必要があり、やや敷居が高いように感じてしまうと思います。
もしかすると、Open-APIの定義が記述されたyamlを開いて会話をしながら、フロントとバックエンド間で課題を共有した後にyamlを変更し、そのプッシュに伴ってCIが自動的に実行されて、必要なファイルが出力され、双方すぐに開発ができるというカッコいいプロジェクトもあるかもしれません。
今回は、Protocol Bufferでその運用ができるように頑張ってみたいと思います。

上の図は、今回目指している運用のイメージです。
「Protocol buffer scheme」というのは、いわゆる「proto」ファイルを管理するGithub上の一つのリポジトリです。
このリポジトリにはJavaやDartなどは言語特有のファイルを一切含まず、このリポジトリだけ見れば、gRPCでやりとりするProtocol Bufferのスキーマ定義がわかるという状態と考えてください。
そこから伸びた矢印の先に、「Interface Package」や「Interface Jar」といった自動で生成されたファイルを管理するinterface用のリポジトリがあります。このリポジトリはスキーマのバージョンニングを行います。
Javaの場合は、Github ActionsでJarを生成して、Github PackagesへJarをプッシュし、Spring Bootなどのバックエンドサーバーは、そのJarに対して依存します。
Flutterの場合はGithubのリポジトリをそのままPackageとして利用するようにpubspec.yamlを書き、タグなどでバージョン管理をします。
仮にPythonやGoで書かれたサーバーやWebフロントが増えたとしても、スキーマ定義が独立していれば、項目の過不足などの詰まらないバグを生むこともないでしょうし、あとはその言語に合わせたコードを生成する部分だけを少し頑張れば良いでしょうから、現実的にこの運用で回すことことはそう難しくはないと考えます。
自動生成されたJavaやDartの変更履歴は残しておきたいのと、一応、人間のチェックは入れないと怖いというのもありますので、インターフェイス用のリポジトリへのプッシュ作業は手動で行います。
Javaの場合は、「proto」ファイルと「java」ファイルを同一のプロジェクトに格納して、protoからjavaを生成するGradleプラグイン「protobuf-gradle-plugin」があります。これを採用しているプロジェクトも多いかと思いますが、前述した通り、「proto」ファイルは共有資産としての独立性を高めたいので、今回はこれを利用しません。
今回は次のリポジトリを「proto」ファイルの格納先として利用します。
現在は良くあるクライアントからのメッセージをオウム返しで応答することを想定した、echoサービスの「proto」ファイル一つが格納されています。
次回以降は、このProtocol Bufferのスキーマを元に記事を書いていきます。
それでは長くなりましたので、今回はこのあたりで終わります。
次回は、「proto」ファイルからDartのコードを自動生成して、パッケージとして管理する部分に関して記述したいと思います。