こんにちは。ソウゾウの Software Engineer (CTO) の @suguru です。連載:「メルカリShops」プレオープンまでの開発の裏側の1日目を担当させていただきます。
7月末にメルカリShopsという新しいサービスが公開されました。メルカリShops は、2021年1月にメルカリのグループ会社として設立したソウゾウが新たに立ち上げたサービスです。
この記事では、メルカリShops を作るにあたり、どういった技術、アーキテクチャを選定したのか、その背景と意思決定をまとめて共有したいと思います。
monorepo
まず最初にプロジェクトをスタートしたときに、サービスのリポジトリを作るのですが、迷わず monorepo による構成を選択しました。monorepo は、システムを構成する複数のコンポーネントの独立性を保ちつつ、全ての構成を1つのリポジトリで管理する手法です。今回の開発でmonorepo を採用した理由はいくつかあります。
システム全体の把握
monorepo の利点は、システムに必要なコンポーネントが全て1つのリポジトリに集約されていることです。git clone を一度実行するだけで、システムの開発に必要なコードがすべて手に入ります。システム全体の把握が容易になり、コンポーネント同士の依存関係や共有コードの管理がしやすくなります。また、参照するコードが常に Single Source of Truth となるため、コンポーネント間のコンフリクトも起きにくくなります。
Pull Request ベース開発との相性
monorepo で最も感じる変化は、Pull Request を作る時かもしれません。通常リポジトリが複数ある場合、Pull Request は変更に影響がある各リポジトリに作り、順序よくマージし、バージョンの依存関係を更新しなければなりません。monorepo では、1つの Pull Request に複数のコンポーネントの変更を含められるため、機能変更の依存管理がしやすく、Pull Requestごとのデプロイ環境の提供が容易になります。実際に、メルカリ Shops では Pull Request ごとに独立した環境をデプロイする機能が稼働しており、 monorepo の恩恵を受けて開発しています。
コミットタイムラインと変更の可視領域
monorepo で開発をすると、全ての変更、Pull Request が目に入ることになります。リポジトリを分割した場合、通常は自分が担当するリポジトリしか見ないことが多いですが、monorepo では自分が普段関わらない領域の変更も目に入ってくるため、システム全体がどういった変更がなされているのか、何が起きているかのトラッキングがしやすくなります。また、タイムラインが1つに統合されるため、コミット履歴を見れば時系列で起きている全体の変更を簡単に把握できます。
Protobuf や Schema との相性
Protobuf を使った開発を経験したことがある場合、生成コードの管理や、protobuf への依存管理に四苦八苦したことがあると思います。 monorepo では、 protobuf が全体の一部として組み込まれるため、protobuf の変更と、アプリケーションコードの変更を併せて行うことができます。さらに、生成コードをリポジトリにコミットする必要もなくなり、proto定義とコードの整合性も考えなくてよくなります。後述する GraphQL のスキーマについても同様のメリットがあります。
メルカリShops のリポジトリ構成
通常 monorepo では、言語に関係なく、機能をベースとして構成するのが一般的だと思います。メルカリShops では、Typescript, Go, Python が主要な言語として使われており、各言語のエコシステムや CI/CD を管理しやすくするため、言語ごとにルートディレクトリを分割することにしました。
monorepo での開発体験記が後日公開予定です。より詳しい内容についてはそちらの記事を参照してください。
Bazel
monorepo を構成する上で、ビルドシステムは重要な選択です。メルカリShops では、実績の多い Bazel を採用しました。
Bazel の良いところ
Bazel の利点は、bazel コマンドさえあれば、monorepo のあらゆるコードをビルドし、実行することができる点です。
monorepo において、各言語ごとの環境を整備したり、ローカル環境を整えることは、なかなか骨が折れる作業です。Bazel は、この環境構築をスキップして、必要なツールやそのバージョンもビルドシステムに組み込んでしまいます。
また、依存関係が管理されているため、あるビルドに必要なビルドも自動で行われます。例えば、protobuf に依存したコードをビルドする場合は、protobuf のコード生成が自動で実行されます。
さらに、Bazel は Cache System が優れているため、差分のビルドが非常に高速です。環境整備は大変ですが、うまく機能したときのビルド速度は気持ちいいくらいに高速です。
Bazel の難しいところ
Bazel の最大の難点は、言語が持つエコシステムを使えないことが多い所です。例えば、Yarn/npm workspaces に対応していないため、それらの機能との併存は困難な壁があります。BUILD ファイルを常に必要とするため、それらのファイルを毎回作り、編集する必要があります。
Go言語は Gazelle のような生成ツールが充実しているため、比較的苦にはなりませんが、他の言語はなかなかそうは行きません。
Bazel が対応していない機能を使う場合、必ず Bazel との格闘が始まります。 そして、Bazel の Sandbox 環境と Starlark 言語を時間をかけて理解し、解決しなければなりません。ここに時間を取られることが、Bazelの難しい所です。
メルカリShopsでは、上記の課題があるために一部 Bazel に対応していないサービスもあります。社内では "Bazel Engineer" が必要だよね、とつぶやいたりしています。
アーキテクチャの全体像
メルカリShops は Google Cloud Platform を利用しており、簡単な全体像は以下のようになっています。
フロントエンドは web ベースになっており、BFFとして GraphQL サーバー、そして Backend は Go や Python で稼働する Microservices 群となっています。データベースは Microservices ごとに独立したデータベースを持っています。データベースは PostgreSQL が主体になっています。
以下に、領域ごとの技術意思決定についてまとめています。
Serverless
ソウゾウでは、エンジニアチームがまだ小さいため、オペレーションの負担を最小化するために、サーバレスアーキテクチャを積極的に採用しています。
例えば、サーバーサイドのほとんどのアプリケーションは、Google Cloud Platform 上の Cloud Run を使って稼働しています。
実際、kubernetes のクラスターやインスタンスはプロダクション稼働環境では利用していません。
また、購買処理などに Cloud Workflow を利用し、ワークフローが必要なケースもサーバレスで処理しています。
Cloud Run はオペレーションの負担が少なく便利ですが、バックエンドプロセスが動かないなど、多くの制約事項があります。この制約によって設計の難易度は上がっています。一方で、制約があるために、必然的にシンプルな設計になり、全体の構成もシンプルになっています。
サーバレスでの運用環境は開発に集中できる一方で、サーバレス特有の制約や問題を解決する必要もあるため、メリット・デメリットが混在します。メルカリ Shops においては、制約がうまく働き、シンプルな構成が維持されているため、kubernetes の複雑な設定やルールを考えなくて良くなる、非常に良い選択でした。
Next.js
メルカリShopsのフロントエンドは全てWebベースになっています。これは、メルカリという既存のアプリに組み込む手段として、独立した環境でシステムを柔軟に構築する手段でもありました。
Webのフレームワークとして選定したのは next.js です。Webである以上ページ表示の速度は何よりも優先されます。CSR, SSR, ISR などの機構を持ち、CDN との構成とも相性が良い next.js はベストな選択でした。
詳細については、後日別の記事で公開予定です。
Microservices
メルカリShopsのバックエンドは、Microservices アーキテクチャを採用しています。言語としては Go が多く、 それぞれのサービスは gRPC を採用しています。サービスローンチ時には、30以上の Microservices が稼働している状態でした。
Microservices を選択した理由は、デプロイや変更の速度と、責任の細分化です。また、gRPC によるインターフェイスの強制化も選定の理由になります。monorepo の恩恵もあり、サービス全体の構造はシンプルで可視性の高い状態が維持できています。
詳細については、後日別の記事で公開予定です。
GraphQL / NestJS
Microservices アーキテクチャを採用する場合、Microservices へのアクセスをシンプルにするためにも BFF (Backend for Frontend) のレイヤーは必須です。
メルカリ Shops では、GraphQL をBFFのインターフェイスとし、NestJS フレームワークを採用しました。多くの依存したコンポーネントにアクセスするために、NestJS の持つ DI(Dependency Injection) のアプローチは相性が良いからです。
また、GraphQL のスキーマ管理は悩ましい課題ですが、NestJS の Code First アプローチはコードに集中することができる良い手法でした。これによってエンジニアはスキーマとコードを同期する必要がなくなり、コードだけを管理すればよくなりました。
PostgreSQL
データベースは Microservices がそれぞれ独立して管理しています。メルカリShops では、PostgreSQL を採用しました。スタート初期は CockroachDB を検討していましたが、 Go で採用した ORM である ent が対応していなかったのと、Cloud SQL での運用を優先し、 PostgreSQL を選択しています。今後 CockroachDB への移転を検討しています。
Code Server
メルカリShopsでは、開発環境として code-server を提供しています。これは、サーバーサイドで稼働する Visual Studio Code で、ブラウザや Chrome App でアクセスすることで Visual Sudio Code に近い開発環境をローカルマシンに提供できます。
クラウドIDEの利点は、ハイスペックなサーバーで稼働できるため、monorepo のように構成コンポーネントが多い場合も高速にビルドやテストをすることができます。メルカリ Shops ではデフォルトで 32コアのサーバー環境を提供しています。
また、ローカルのPCにも負担をかけないため、開発にかかるCPU負荷から解放されますし、スペックの低いラップトップでもインターネットさえ繋がっていれば、快適に開発ができます。サーバー自体もクラウドネットワーク内にあるため、高速なネットワーク環境下にあると共に、開発環境へのネットワークアクセスもできるため、いろいろと便利なことが多いです。
さらにローカルマシンが複数あっても、サーバー側に常に開発状況が維持されているため、それぞれの環境にいろいろな開発用ソフトウエアをセットアップする必要もありません。ブラウザさえあれば動作するところも便利です。
Vertex AI
サービスローンチ時にはAIに関する機能要件はなかったのですが、ローンチ後の需要を予測し、AI, Machine Learning の機能開発は進めていました。開発中に丁度 Google が発表した Vertex AI プラットフォームが使えるようになったため、MLエンジニア @wakanapo の提案もあり、Vertex AI プラットフォームを活用して開発を進めています。MLチームの活動については、後日記事にて公開される予定です。
まとめ
ここまで全体の技術スタックを紹介しました。メルカリShops では、エンジニアの DX (Developer eXperience) を最大化することを意識した技術選定になっています。昨今では大量トラフィックの対応や可用性、対障害性などの問題は、クラウドプラットフォームが提供する機能によって解決することが多くなっています。Cloud Native な環境での開発では、何よりもエンジニアの生産性や開発することの楽しさを優先することが良いだろうと考え、上記のような技術を選択してきました。
このチームが行ってきた技術の意思決定が、これからプロジェクトをスタートする方々へ、少しでも有益な情報にになれば幸いです。
後日公開する記事で、より詳細な情報に触れていきます。具体的な内容を知りたい方は、今後の記事にぜひご期待ください。
一緒に作っていくメンバーを募集中
我々は常に新たな手法、技術を模索し、環境を改善し続けています。変化は激しいですが、エンジニアにとっては、かなり刺激のある環境になっています。プロダクトとしても技術側でもやりたいこと、挑戦したいことがたくさんあります。ありがたいことに、多くのお客さまがサービスの利用をスタートしています。エンジニアとして変化の激しい環境に身を置きつつ、多くの方に価値のあるものを提供していきたい、そんな方にとっては素晴らしい環境になっていると思います。
ソウゾウではエンジニアを絶賛募集中です。もし上記のような環境に少しでも興味があれば、下記からご連絡ください。
エンジニアとして一緒に働いてみたい方はこちら
https://careers.mercari.com/jp/search-jobs/?cat=souzoh-inc
ソウゾウのメンバーとカジュアルに話してみたい方はこちら
https://docs.google.com/forms/d/e/1FAIpQLSe_YPZc0uj0gCGymBGPWQv9zNEjNUMYeQwQyaQAPuvIhaQ7dQ/viewform