マルチプロダクトを支える、gRPCとポリレポにおけるprotoファイル一元管理の戦略

はじめに
こんにちは、ログラスでエンジニアをしております、田中です(@kakke18_perry)。
ログラスはマルチプロダクト戦略を掲げており、その実現に向けた共通基盤の構築を推進しています。
この戦略の実現を見据えて、以下の2つの技術的な決定をしました。
- サービス間通信にはgRPCを利用する
- protoファイルと自動生成ファイルは共通リポジトリ(以後、共通protoリポジトリ)に集約する
本記事では、この2つの決定に至った背景と理由、そしてポリリポ環境でどのように共通protoリポジトリを運用し、いかに多言語間の型安全を持続可能に実現しているかについて、具体的なノウハウを交えて解説します。
マルチプロダクトでの課題と、protobuf(gRPC)が持つ優位性
マルチプロダクト戦略の推進において、将来的に通信方式がボトルネックになることはこれまでの経験や他社の事例から明らかでした。そのため、今後のプロダクト展開を見据えてサービス間通信において、REST(OpenAPI)よりもprotobuf(gRPC)が以下の3点において優位であると判断し、導入しました。
1. 厳格なスキーマ定義と型安全性の確保
protobufは厳格なスキーマ定義が必須で、そこからコードが自動生成されるため、常に定義と実装が同期されます。OpenAPIと比較して簡素に記述でき、可読性が高く、Bufなどのツールも充実しています。
2. 後方互換性を保ちながらの開発の容易さ
protobufは、フィールドのタグ番号の管理を通じて、後方互換性を保ったままインターフェースを柔軟に進化させやすい設計です。これにより、運用コストの高いRESTのバージョン管理を避け、破壊的変更の混入を防ぐことが可能になります。
3. 将来的な通信要件への対応力
gRPCはHTTP/2をベースとしており、ストリーミング通信を標準でサポートしています(導入から半年経った現在、PoC実装などで既に活用中)。protobufによる軽量なバイナリシリアライゼーションは、通信量が増大する将来においても優位であると考えました。
共通化の課題と共通protoリポジトリによる実践
Fail Fastで発見した、初期の運用課題
ログラスではプロダクトごとにGitHubリポジトリを分けるポリリポを採用しています。その中で「protoファイルや自動生成ファイルをどこに配置し、複数のリポジトリから参照するか?」という課題が発生しました。
ログラスのバリューである「Fail Fast」(問題点を素早く見つけ、すぐに改善に向かうという価値観)の精神に基づき、まずは最もシンプルな運用から開始しました。
- 手動コピー&ペースト: protoファイルを、それを利用する各サービスリポジトリに手動でコピー&ペーストしていました。
- 個別でコード生成: 各リポジトリ内で、bufを用いて個別にコード生成を実行していました。
最もシンプルな方法ではあったものの、サービス数が2、3の時でさえ、手動によるファイル同期の運用コストが徐々に無視できなくなってきました。
共通protoリポジトリへの集約と構造的メリット
この課題解決のため、protoファイルと自動生成ファイルを共通protoリポジトリに集約しました。これにより以下のようなメリットがありました。
- protoファイルは共通protoリポジトリが唯一の情報源となり、各リポジトリでファイルが異なるミスがなくなります。
- 各サービスリポジトリは、コード生成ツールや複雑なコマンドを意識する必要がなくなり、本質的な業務ロジックに集中できるようになりました。
- コード生成の責務が共通protoリポジトリに集約され、メンテナンスコストが大幅に削減されました。
この共通protoリポジトリの導入は、ポリリポ構成を維持しつつ、モノリポのような一貫性と型安全性の恩恵を享受するための決定的な一歩となりました。
Kotlin/Goへの具体的な配布方法
protoファイルと自動生成ファイルを共通protoリポジトリに集約しましたが、その自動生成コードを各サービスリポジトリがどのように参照しているかについて説明します。各言語の標準的な依存管理フローに統合できる配布方法を採用しました。
Kotlinサービスへの配布:Mavenリポジトリとして公開
Kotlinで実装されたサービスへは、共通protoリポジトリ内で生成された Kotlinコードを、Mavenリポジトリとして公開する手法を採用しました。各リポジトリでは、build.gradle.ktsでMavenリポジトリを指定し、通常の依存関係として参照します。この方法により、開発者は他のライブラリと同様に、Kotlinの標準的な依存管理を通して自動生成コードを利用できます。
Goサービスへの配布:タグ付けによるライブラリ公開
Goで実装されたサービスへは、Go Modulesの仕組みを利用し、共通protoリポジトリリポジトリ自体をGoライブラリとして公開する手法を採用しました。各リポジトリでは、go.modで共通protoリポジトリのタグ付きバージョンを指定し、依存関係として参照します。
共通化によるデメリットとその対応
protoファイルと自動生成ファイルを共通protoリポジトリに集約し、多言語への自動配布の仕組みを整備したことで、マルチプロダクト戦略に必要な型安全で持続可能なインターフェース管理の仕組みが整いました。
しかし、このような共通化には、メリットだけではなく、必ずデメリットも存在します。
共通化がもたらす短期的なデメリット(コスト増)
共通リポジトリ導入による最大のデメリットは、開発プロセスに一時的なオーバーヘッドが発生することです。
共通リポジトリへ移行すると、protoファイルに変更が必要な場合、開発者は共通protoリポジトリを修正し、公開フロー(ビルド→Maven/Tag公開)を経る必要があります。これは、初期の柔軟な開発と比べて時間と工数のかかる手順となり、短期的な開発スピードを鈍らせる要因となります。
デメリットに対する現実的な運用と対策
デメリットであるオーバーヘッドを回避するため、サービスの開発初期において、まだクライアントから呼び出されない段階では、サーバー側のリポジトリ内でprotoファイルを管理し、柔軟に開発を進めます。そして、クライアントからの呼び出しが必要になるタイミングで、protoファイルを共通protoリポジトリへ移行し、正式な共通インターフェースとして公開するという運用を取っています。
さいごに
本記事では、マルチプロダクト戦略の実現を見据えた「gRPCの採用」と「共通protoリポジトリによるprotoファイル一元管理」という2つの決定、そしてその具体的な運用ノウハウを共有しました。
ログラスはマルチプロダクト戦略をさらに推進しています。技術的な決定だけでなく、それを組織全体で支えるためのイネーブリングやプラットフォーム構築の活動も並行して進めています。
こうした、技術基盤の構築や、挑戦的なマルチプロダクト戦略の推進に興味がある方、一緒にチャレンジしたいという方は、ぜひ一度お話しさせてください!
Discussion