Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

コンパウンドプロダクト開発の質とスピードを支える Protobuf と Connect #アー...

コンパウンドプロダクト開発の質とスピードを支える Protobuf と Connect #アーキテクチャ_findy / Boosting Compound Product Development Efficiency with Protobuf and Connect

2024-05-22 アーキテクチャを突き詰める Online Conference
https://findy.connpass.com/event/314782/

■ 参考URL

- コンパウンドスタートアップというLayerXの挑戦|福島良典 | LayerX
- https://comemo.nikkei.com/n/n7332c93f50c7
- ビジネスドメインの拡大を実現する バクラクシリーズでのモノレポ開発 - Speaker Deck
- https://speakerdeck.com/layerx/bakuraku-devsumi-2024-yyoshiki41
- Connect
- https://connectrpc.com/
- gRPC
- https://grpc.io/
- Protocol Buffers によるプロダクト開発のススメ - API 開発の今昔 - | Wantedly Engineer Blog
- https://www.wantedly.com/companies/wantedly/post_articles/309513

Masayuki Izumi

May 22, 2024
Tweet

More Decks by Masayuki Izumi

Other Decks in Programming

Transcript

  1. © LayerX Inc. 2 ▸ Wantedly, Inc. (2018-04 - 2022-08)

    ▸ LayerX (2022-09-) ‐ バクラク事業部 Enabling Team ‐ Backend と Web Frontend 中心にやってます ▸ 最近のお気に入りパッケージは @bufbuild/[email protected] 画像を入れてね whoami @izumin5210
  2. © LayerX Inc. 7 ▸ プロダクト: お客様に1つのパッケージとして価値を提供するソフトウェア製品 ‐ (「バクラク申請」など、右下の図における ◦

    を指すイメージ) ▸ サービス: 論理的あるいは物理的に分けられた1つのサーバアプリケーション ‐ マイクロサービス・アーキテクチャにおける 「サービス」という単語と概ね同等 発表内での用語の定義 コンパウンドプロダクトの開発の大変ポイント
  3. 8 © LayerX Inc. 今日の話の前提: バクラクについて 請求書受取・仕訳 申請・経費精算 電子帳簿保存 請求書発行

    ビジネスカード Service Service Service Service(組織情報等) Service Service ▸ プロダクトごとに1つ以上のサービス*がある ▸ あらゆるプロダクトから利用される情報は サービスが分かれていたりする * 本発表では「サービス」という単語をマイクロサービス・アーキテクチャにおける 「サービス」と同じような意味で使っていきます
  4. 9 © LayerX Inc. 今日の話の前提: バクラクについて - 共通のデータを元に統合されたユーザ体験 #とは 請求書受取・仕訳

    申請・経費精算 電子帳簿保存 請求書発行 ビジネスカード ▸ カード利用と稟議の紐づけをラクにしたい ▸ カード利用後の報告(領収書等の提出)をラクにしたい ‐ 忘れてたら教えてほしい ▸ ... 「共通のデータを元に統合されたユーザ体験」の例
  5. 10 © LayerX Inc. 今日の話の前提: バクラクについて Service Service Service(組織情報等) Service

    Service Service プロダクト間連携を実現するために サービスが増えることもある ※ 細かい構成は今回の話では重要ではないため仮のものです Service Webapp バクラク申請 カードや利用明細を 申請時に見たい カードの明細を取りに行ったりする 非同期の処理や通信もある(e.g. 通知) サービスまたぐ・またがないどちらもある
  6. © LayerX Inc. 11 ▸ プロダクトが複数ある 👉 サービスが複数ある ▸ プロダクト間の連携がある

    👉 サービス間の通信がある ‐ サービスがうまく通信・連携することがそのまま価値の拡張につながる ‐ 通信・連携もいろいろ ‐ フロントエンド - (自分の|隣の)バックエンド ‐ バックエンド - バックエンド ‐ 同期 ‐ 非同期 コンパウンドプロダクトの開発 コンパウンドプロダクトの開発の大変ポイント
  7. © LayerX Inc. 13 ▸ 今日はあまりに時間がないので、他の資料を参考にしていただけると… 🙏 ‐ コンパウンドスタートアップというLayerXの挑戦 *

    ‐ ビジネスドメインの拡大を実現する バクラクシリーズでのモノレポ開発 ** ▸ もしくは OpenDoor(カジュアル面談)などで聞いて下さい! おまけ: 結局コンパウンドって何?マルチと何が違う? コンパウンドって何 * https://comemo.nikkei.com/n/n7332c93f50c7 ** https://speakerdeck.com/layerx/bakuraku-devsumi-2024-yyoshiki41
  8. © LayerX Inc. 15 ▸ gRPCと互換性をもつ、 HTTP APIを実装するためのフレームワーク ▸ gRPC

    Protocolとの完全な互換性に加え、 human-readable and debuggableなConnect Protocolをサポート ‐ JSONやProtobufをHTTP/1.1の上でやりとりできる(片方向通信の場合) Connect #とは Protobuf と Connect https://connectrpc.com/
  9. © LayerX Inc. 16 ▸ 強力な型安全性: Protocol Buffers(protobuf)を使用してデータをシリア ライズし、厳密な型チェックと効率的なデータ交換を実現します。 ▸

    サービス定義の自動生成: サービスインターフェースとメッセージ定義から自動 的にクライアントとサーバーのコードを生成します。 ▸ (高性能, 複数言語の対応, 双方向通信 も挙げられたが、ここでは割愛) gRPC #とは (gRPCの特徴 by ChatGPT) Protobuf と Connect
  10. © LayerX Inc. 17 gRPCのコード生成 Protobuf と Connect message GreetRequest

    { string name = 1; } こういうmessage定義から… type GreetRequest struct { // private fields Name string `...` } こういう構造体定義が生成される
  11. © LayerX Inc. 18 gRPCのコード生成 Protobuf と Connect service GreetService

    { rpc Greet(GreetRequest) returns (GreetResponse) {} } こういうサービス定義から…
  12. © LayerX Inc. 19 gRPCのコード生成 Protobuf と Connect type GreetServiceClient

    interface { // クライアントがリクエストするとき の interface Greet(context.Context, *connect.Request[v1.GreetRequest]) (*connect.Response[v1.GreetResponse], error) } type GreetServiceHandler interface { // サーバが実装することになる interface Greet(context.Context, *connect.Request[v1.GreetRequest]) (*connect.Response[v1.GreetResponse], error) } こういうのを生成してくれる(これは connect-go の生成物の一部) ボイラープレート的な処理はgRPCやConnectがうまくやってくれる
  13. © LayerX Inc. 20 Protobuf + Connect(gRPC) 嬉しいポイント① Protobuf と

    Connect ▸ 実装がサービス定義に従うことが保証される ▸ Protobuf により互換性が保たれやすい・壊れても Lint で気付ける ‐ 隣のサービスは別のチームの持ち物であるとき、これは特に重要 ▸ ボイラープレートから解き放たれる ‐ サーバ実装ではもう json.Unmarshal とかしなくていいし、型も信用できる クライアントもサービス定義そのままな interface を叩くだけ
  14. © LayerX Inc. 21 Protobuf + Connect(gRPC) 嬉しいポイント① 続き Protobuf

    と Connect ▸ それなりに直感的に書けるIDL(Interface Definition Language) ‐ cf. yaml とかで書くよりは、プログラムを書いてる気持ちで書けるので楽(書く気になる) ▸ Protobuf IDLによるスキーマ定義が「コミュニケーションツール」となり、 「ドメイン理解促進のための議論の場・ドキュメント」になる * ‐ コード生成やIDL自体が複雑なシステムでもスピード(開発速度)を保ち、 かつ質(内部品質)も維持するための武器になる * Protocol Buffers によるプロダクト開発のススメ - API 開発の今昔 https://www.wantedly.com/companies/wantedly/post_articles/309513
  15. © LayerX Inc. 22 Protobuf + Connect 嬉しいポイント① Protobuf と

    Connect 「隣のサービスから叩かれるAPIを作る」「隣のサービスのAPIを叩く」 「隣のプロダクトの情報をフロントで見せる」「非同期の処理やAPIを作る」 「(そもそも)複数のサービスをローカルで動かす」 👉 Connect(gRPC)の基本的な要素で、上で強調している2つは軽減できる!
  16. © LayerX Inc. 26 新しいサービス作るの面倒問題 Connectでローカル開発を楽にする 新しいサービス 作るの面倒 ▸ 新しいサービスを増やすのはだいたい面倒

    ‐ 適切な場所にディレクトリ切って、 エントリポイントのファイルおいて、 必要なミドルウェア差し込んで、、、 ‐ インフラの準備とかもある
  17. © LayerX Inc. 27 新しいサービス作るの面倒問題 Connectでローカル開発を楽にする 新しいサービス 作るの面倒 ▸ .protoからサービスのボイラープレートをscaffoldする

    ‐ 1 gRPC service = 1サービス というのを制約として置く ‐ それだけ決めておけば、Proto Package, gRPC Service から 新しいサービスを立ち上げるための諸々をガバっと生成できる ‐ ちゃんと graceful shutdown したり、 共通のロガーや認証のミドルウェアを入れたり、 …
  18. © LayerX Inc. 28 gRPCデバッグ面倒問題 Connectでローカル開発を楽にする gRPCのデバッグ 地味に面倒 ▸ gRPCではcURLなどHTTP

    APIのデバッグ技法は使えない ‐ grpcurlやEvansなどの便利なツールはあるし、 PostmanやInsomniaはgRPCにも対応している それでも、何かしら専用のツールなり設定は必要になる
  19. © LayerX Inc. 29 gRPCデバッグ面倒問題 Connectでローカル開発を楽にする gRPCのデバッグ 地味に面倒 ▸ ConnectではcURLが使える!

    ‐ JSON over HTTP/1.1でもリクエストを受けられるので、 普段使ってるツールや知識で戦える範囲が広い!
  20. © LayerX Inc. 31 サービスいっぱい起動するの面倒問題をなんとかする Connectでローカル開発を楽にする curl \ --header "Content-Type:

    application/json" \ --data '{"name": "izumin5210"}' \ http://localhost:3000/greet.v1.GreetService/Greet ConnectサーバにcURLでリクエストする例
  21. © LayerX Inc. 32 サービスいっぱい起動するの面倒問題をなんとかする Connectでローカル開発を楽にする curl \ --header "Content-Type:

    application/json" \ --data '{"name": "izumin5210"}' \ http://localhost:3000/greet.v1.GreetService/Greet Protobufのパッケージ+ gRPCのサービス名 👉 サービス毎にpathの先頭が異なることが保証できる
  22. © LayerX Inc. 33 ローカルでは1つのHTTP serverに 全Connect serverを乗せる Connectでローカル開発を楽にする TenantService

    NotificationService CardTransactionService 論理的には別のサービス ローカルでは物理的には1つのサービス TenantService NotificationService CardTransactionService 社内ではgo-allと呼ばれています
  23. © LayerX Inc. 34 ローカルでは1つのHTTP serverに 全Connect serverを乗せる Connectでローカル開発を楽にする TenantService

    NotificationService CardTransactionService ▸ ローカルでは全サービスを1つに まとめることで、起動を楽にしている ▸ cURLで雑デバッグしたいときも ポート番号を1つだけ覚えておけばいい ▸ (論理的なサービス単位と物理的なサービス単位を 分けて考える話は本番環境でも使える)
  24. © LayerX Inc. 35 Protobuf + Connect 活用ポイント② Protobuf と

    Connect 「隣のサービスから叩かれるAPIを作る」「隣のサービスのAPIを叩く」 「隣のプロダクトの情報をフロントで見せる」「非同期の処理やAPIを作る」 「(そもそも)複数のサービスをローカルで動かす」 👉 制約をつくることで、gRPC service から新しいサービスをscaffoldできる! 👉 Connectなので雑cURLデバッグも可能! 👉 ローカルではサービスを1つにまとめることで、いろんな複雑さから開放される!
  25. © LayerX Inc. 37 非同期API・非同期ジョブ Connectで非同期APIも楽にする ▸ だいたい必要になる ‐ ここで扱うのはscheduledなものではなく、on-demandなもの。

    e.g. 通知送信 Amazon SQS等の外部のキューを利用するなどして実装する ▸ デバッグが面倒になりがち ‐ テストでカバーできるならそれでもいいけど、 外部連携等あるとつなぎ込んでの動作確認をやりたくなりがち… ‐ REPLがない言語で非同期処理をどうやって雑にエンキューするか問題
  26. © LayerX Inc. 38 非同期API・非同期ジョブにもConnectを Connectで非同期APIも楽にする Publisher Connect server service

    GreetService { rpc Greet(GreetRequest) returns (GreetResponse) {} } 行き先(GreetService/Greet)と ペイロード(GreetRequest)を キューに詰める Subscriber Worker
  27. © LayerX Inc. 39 非同期API・非同期ジョブにもConnectを Connectで非同期APIも楽にする Publisher Connect server Subscriber

    Worker キューから取り出した内容を見て Subscriber が適切な Connect Server を呼ぶ ここがConnectによる通信になる
  28. © LayerX Inc. 40 この仕組みがなぜ嬉しいか Connectで非同期APIも楽にする Publisher Connect server Subscriber

    Worker ▸ 非同期でもちゃんとスキーマ定義できる ‐ 雑に json.Marshal するのではなく、 同期APIと同じメンタルモデルで型定義を。 非同期ジョブ・非同期APIもAPI! ▸ 雑デバッグが楽! ‐ cURL最高!
  29. © LayerX Inc. 41 Protobuf + Connect 活用ポイント③ Protobuf と

    Connect 「隣のサービスから叩かれるAPIを作る」「隣のサービスのAPIを叩く」 「隣のプロダクトの情報をフロントで見せる」「非同期の処理やAPIを作る」 「(そもそも)複数のサービスをローカルで動かす」 👉 非同期APIでもProtobuf+Connectによるスキーマ定義や実装を使い デバッグの手間や認知負荷を軽減する 型を信用できるようにもなる!
  30. © LayerX Inc. 43 おさらい: 隣のプロダクトの情報をフロントで見みたい 隣のプロダクトの持つ情報をフロントで見たい Service 組織情報など Service

    カード明細など Service 申請情報など Webapp バクラク申請 ▸ 連携パターンが増えると、 この要求も増える ▸ プロダクトごとのBFFを作る? ‐ たとえば「ユーザ情報見たい」とかは 全プロダクト共通だけど、それも毎回 BFFに作る? ▸ データの連携 → 関連するデータの つながりを引っ張ってこれればよい
  31. © LayerX Inc. 44 アイディア: 1つのGraphQLスキーマに集約してしまう 隣のプロダクトの持つ情報をフロントで見たい Service 組織情報など Service

    カード明細など Service 申請情報など GraphQL Gateway バクラク申請 Webapp バクラクの持つ情報を1つのグラフで表現 隣のプロダクトの情報がほしいときはグラフをつなげる
  32. © LayerX Inc. 45 問題: GraphQLとConnect(Protobuf)でスキーマが分裂する 隣のプロダクトの持つ情報をフロントで見たい Service カード明細など GraphQL

    Gateway message CardTransaction { // 決済金額 int32 amount = 1; } type CardTransaction { """決済金額""" amount: Int! } フィールドの更新追従も面倒だが、ドキュメントの同期とかは考えたくない… ドキュメントを書くモチベーションも下がってしまう
  33. © LayerX Inc. 46 対応: ProtobufからGraphQLの型を生成する 隣のプロダクトの持つ情報をフロントで見たい message CardTransaction {

    // 決済金額 int32 amount = 1; } type CardTransaction { """決済金額""" amount: Int! } GraphQLのObjectやEnumなどの型はProtobufから自動生成することで、同期の手間を省く Protobufはコード生成ツールを自作するのも比較的カンタン .proto .graphql * gRPC → GraphQL 変換だとGraphQL MeshなどのOSSもありますが、 今回は@izumin5210が自作したprotoc-gen-pothosを使いました
  34. © LayerX Inc. 47 Protobuf + Connect 活用ポイント③ Protobuf と

    Connect 「隣のサービスから叩かれるAPIを作る」「隣のサービスのAPIを叩く」 「隣のプロダクトの情報をフロントで見せる」「非同期の処理やAPIを作る」 「(そもそも)複数のサービスをローカルで動かす」 👉 情報を1つのGraphQLスキーマに集約!型はProtobufから自動生成!
  35. © LayerX Inc. 49 まとめ ▸ コンパウンドプロダクト(複数プロダクト)環境で開発がしんどくなっていく問題を、 ProtobufとConnectの力で軽減するようにした ‐ プロダクトの質・コードの質を高く保つ、でもスピードもなるべく落とさず、スケールしていく!

    ▸ 具体的な5つの問題を軽減するために、ConnectとProtobufを活用した ‐ 「隣のサービスから叩かれるAPIを作る」「隣のサービスのAPIを叩く」 ‐ Connect(gRPC)のコード生成による安全かつ生産性の高いAPI開発 ‐ 「(そもそも)複数のサービスをローカルで動かす」 ‐ 論理的なサーバ単位と物理的なサーバ単位を分けて考え、ローカルでは1つのサーバに集める ‐ 「非同期の処理やAPIを作る」 ‐ キューは挟みつつ、非同期APIもConnectで実装することでデバッグの手間や認知負荷を軽減 ‐ 「隣のプロダクトの情報をフロントで見せる」 ‐ 集約層としてGraphQLを導入しつつ、スキーマ2重定義を避けるためにコード生成を使う
  36. © LayerX Inc. 50 おまけ: Protobuf その他の活用 Protobuf と Connect

    ▸ ログ用に効率の良い ObjectMarshaler を生成 ▸ FeatureFlagをProtobufで定義 ▸ ConnectのRPCハンドラのscaffolding ▸ ちょっと便利なgRPC clientのコンストラクタを生成 ‐ gRPCサービスのURLを解決する仕組みが入ってたり, 共通のinterceptorが入っていたり… ▸ … ほかにもいろいろ!
  37. © LayerX Inc. 51 We are hiring! layerx.go #0 LayerX

    Open Door 今日の内容で気になり・ツッコミ・議論したいことがあった方! カジュアル延長戦もお待ちしております 🙏 layerx.go 5/24(金) 19:00〜20:00 @ onlineにて実施します。 「Goの情報収集や知見の共有活動 / Goのプロダクトコー ドのキャッチアップ」というテーマで複数のLT(公募含む)を 発表予定です!