チャットコマースサービス「CARU」の技術選定プロセス紹介 & そこから得た学び
はじめに
CyberACE テックブログ、第1回目の投稿です!🎉
CyberACE でチャットコマースサービス「CARU」の開発をしている tfuka と申します。
このたび新規サービスを1から立ち上げる機会に恵まれ、4-5人の開発メンバーで約9ヶ月かけて CARU をリリースしました。この記事では、開発初期の技術選定プロセスと、そこから得られた学びを共有します。
CARUとは
CARU は、企業のランディングページ(LP)を訪問したユーザーに対して、チャットを通じて商品をレコメンドするチャットコマースサービスです。
初期リリースでは、離脱しそうなユーザーを検知してポップアップを表示し、チャット経由でコンバージョンにつなげる機能を実装しました。ビジネスモデルは成果報酬型で、獲得したコンバージョン数に応じて顧客企業から料金をいただく仕組みです。
システム要件
CARU の開発にあたって、以下の要件を満たす必要がありました。
パフォーマンス要件
顧客企業がテレビCMなどの大規模なプロモーションを実施した際、短期間で急激にアクセスが増加することが想定されます。通常時のトラフィックから数倍〜数十倍のスパイクが発生する可能性があるため、このような急激な負荷変動に対応できるシステム設計が必要でした。
具体的には以下の要件を満たす必要がありました:
- DAU数万規模のユーザーアクセスに耐えられる構成
- スケーラビリティ: トラフィック増加に応じた自動的なスケールアウト
- スパイク耐性: 急激な負荷変動でもサービスが停止しない設計
開発・運用要件
CARU 開発チームはまだ人が少ないため、パフォーマンス要件に加えて、以下の要件も満たすことが必要でした。
- 開発コストを抑える
- 運用・メンテナンスコストを抑える
- 将来の機能追加や仕様変更に柔軟に対応できる構成
技術選定プロセス
チャットプラットフォーム: LINE
チャット機能を自社開発するのではなく、既存のプラットフォームを活用することで初期開発工数を削減する方針を採りました。
日本市場でのシェアを考慮し、LINE をチャットプラットフォームとして採用しました。
初期アーキテクチャ設計
開発の最初期段階では何もわからないため、とりあえず以下のような構成から始めました。
加えて、以下の要素を考慮していきます。
- LINE からの大量リクエストに耐えうる必要がある
- 管理画面からのトラフィックは相対的に無視できそう
- 将来の拡張を見据えてできるだけマイクロサービスに寄せる
プログラミング言語: Go
プロジェクト開始時点で、採用の観点から Go 言語でほぼ決定していました。
Go を選択した理由:
- 高パフォーマンス・効率的なリソース利用
- エンジニア採用市場での人気と学習コストの低さ
API アーキテクチャスタイル: gRPC
本プロジェクトはビジネス要件と技術要件の両面で不確実性が高かったため、将来の拡張性を重視してビジネスロジックを gRPC で囲う選択をしました。
gRPC は REST よりもサーバー間通信のパフォーマンスがよく、将来的な要件変更の際にパフォーマンス面での制約・対応工数が少なくなるはずです。
クラウドプラットフォーム: GCP
当時注目を集めていた Cloud Run を中心に検討を進めました。
Cloud Run 選定の主な理由:
- コンテナベースのオートスケーリング機能
- フルマネージドサービスによる運用負荷の軽減
- 標準的なコンテナ技術の活用が可能
さらに、技術検証を行い Cloud Run で gRPC が問題なく使えることを確認し、GCP の採用を決定しました。
データベース: Firestore
高負荷に対応できるデータベースが必須要件でした。RDBMS の強い整合性よりも、NoSQL のスケーラビリティとパフォーマンスを優先しました。
最終的に MongoDB と Firestore を比較検討し、以下の理由で Firestore を採用しました。
- フルマネージドサービスで運用負荷が最小限
- 自動スケーリングによるトラフィック変動への対応
- GCPエコシステムとの親和性
MongoDB の場合、シャーディングやレプリケーションの設定・運用が必要となり、初期フェーズでは過剰な運用負荷になると判断しました。
管理画面: React, Next.js
管理画面は限られた人数のみがアクセスし、トラフィックも LINE 経由のアクセスと比較して無視できるほど小さいため、技術選定における制約は特にありませんでした。そのため、開発メンバーが慣れている React と Next.js を採用し、Cloud Run に乗せる構成としました。
gRPC プロキシ: envoy, grpc-gateway
gRPC を使うにあたり、envoy と grpc-gateway という技術を追加で採用しました。これらはほぼデファクトスタンダードになっているため、特に比較検討は行いませんでした。
envoy
gRPC プロトコルは HTTP/2 に依存していますが、ブラウザからのリクエストは HTTP バージョンに依存しない仕様になっているため、そのままでは通信できません。
そこで grpc-web という、HTTP バージョンに依存しないプロトコルを使用し、envoy プロキシで gRPC プロトコルに変換する構成を採用しました。
grpc-gateway
LINE 関連のイベントは Webhook(HTTP/1.1)で送信されるため、gRPC サーバーと直接連携することができません。
そのため、HTTP と gRPC のプロトコル変換を行うゲートウェイサーバーを用意し、LINE からの Webhook リクエストを ゲートウェイサーバーに中継する構成としました。
開発前半のシステムアーキテクチャ
これらの技術選定を経て、開発前半では以下のアーキテクチャを構築しました。
ここからリリースまでには追加のサービスやリソースを実装しましたが、そちらは機会があれば別記事でご紹介します。
やりきれなかったこと
開発前半を振り返ると、いくつかの技術的課題が残りました。
しかし、これらの課題はありつつも、システムとしては要件を満たし、安定稼働を実現できています。
1. Connect の未採用
Cloud Run と gRPC の検証後に、gRPC の代替として注目されている Connect という技術の存在を知りました。開発の後半フェーズで導入を検討しましたが、既にサービスが動作している状態だったことと、移行に必要な開発工数を考慮し、採用を見送りました。
Connect のクライアントライブラリはよくできており、フロントエンドの開発工数削減が期待できましたが、リプレイスのリスクとメリットを総合的に判断した結果、現状維持を選択しました。
2. grpc-gateway の技術負債
REST API と gRPC の変換層として導入した grpc-gateway は、変更頻度は低いものの、メンテナンス性に課題を抱えています。
Cloud Run と gRPC の検証時にゲートウェイも実装しましたが、本来活用すべき grpc-gateway のコード生成機能が使われておらず、HTTP と gRPCの処理のつなぎ部分が自前実装になっていました。当時の僕には gRPC の知識が不足していたため、レビュー時にこの問題を見落としてしまいました。
その結果、新たな HTTP エンドポイントを追加する際に、本来不要な追加工数が発生する状態になっています。
3. アーキテクチャの分離不足
主に gRPC 周りのコードの責務分離が不十分で、アーキテクチャ図から分かる通り、envoy、Gateway、Backend がすべて同じ Cloud Run インスタンスに同居しています。
この構成により、以下の課題が生じています。
- envoy,Gateway,Backendのログが同一サービスのログとして出力されるため、ログの分析が少々しづらく、デバッグ時の効率に影響している
- LINE と管理画面のトラフィックが同一のルートを通るため、LINE 側の負荷が増大した際に管理画面へのアクセスが困難になる可能性がある
エンジニアリングの観点では改善すべき点ですが、現時点ではビジネス要件を優先し、この構成での運用を続けています。
学びと振り返り
実は前述の技術的課題は、CARU のリリース後でも解決できていません。開発の後半フェーズで技術的負債を解消する予定でしたが、負荷検証の結果、現状のシステムがビジネス要件を十分に満たしていることが判明したため、改修を見送りました。
要件が満たされているのであれば、開発リソースは新機能などのビジネス的に有効なものに割り当てた方が良いはずです。
この経験から、以下の学びを得ました。
初期設計の重要性
開発初期であっても、将来を見据えた設計が必要だと感じました。
- 技術的負債と開発スピードのバランスを取りつつも、最初からある程度しっかりとしたアーキテクチャで構築することが重要
- 初期の技術選定は、実質的に後から変更することが極めて困難
- リリース前は「まずは動くものを」という圧力があり、リリース後は新機能開発が優先されるため、基盤部分の大規模な改修機会はなかなか訪れない
エンジニアの腕の見せどころ
とは言っても、最初から正しい設計で進めることは現実的に困難かつ非効率なので、初期は仮設計で進めつつ、傷が広がらないうちに設計を正しい形に直すことが重要です。そこがある程度遅れると、現実的に直せるタイミングはかなり後になってしまいます。
結論、いかに序盤工数をかけずに、要件を満たしつつ、将来の技術負債を潰せるかがエンジニアの腕の見せどころであると感じました。
おわりに
この記事では、CARUの立ち上げから約9ヶ月間の技術選定プロセスと、そこから得られた学びを共有しました。
新規サービスの開発は、理想と現実のトレードオフの連続でした。Go、gRPC、Cloud Run、Firestoreという技術選定は、結果的に大規模トラフィックにも耐える堅牢なシステムを実現できました。一方で、Connectの採用見送りなど、いくつかの技術的課題も残っています。
しかし、これらの課題を抱えながらもビジネス要件を満たせているという事実は、エンジニアリングの本質を考えさせられます。技術的完成度の追求も大切ですが、最終的にはビジネスへの貢献が何より重要だということを学びました。
今後も新機能開発を進めながら、適切なタイミングで技術的負債の解消にも取り組んでいきます。この経験が、新規サービス立ち上げに挑戦される方々の参考になれば幸いです。

株式会社CyberACEのテックブログです。開発チームが日々の業務で得た技術的な学びや知見を発信します。主な技術スタック: TypeScript, Go, React, Next.js, AWS, GCP cyberace.co.jp
Discussion