Mercari Engineering Blog

We're the software engineers behind Mercari. Check out our blog to see the tech that powers our marketplace.

メルカリの新しいホーム画面を支えるマイクロサービス

この記事は、 Mercari Bold Challenge Monthの13日目の記事です。

こんにちは、メルカリアプリのホーム画面のバックエンド開発を担当している @akkie30 です。

六本木の喧騒を離れた千葉県の温泉宿でこの記事を書いています。この記事を書く前はマザー牧場に行って動物達と触れ合い、ISUCONの予選を突破できなかった悔しさをウサギやモルモットと触れ合うことで洗い流していました。来年は頑張って予選突破したいと思います。

さて、唐突ですがメルカリのアプリを起動した際に表示されるトップページはホーム画面と呼ばれています。 今回の記事では、このホーム画面を刷新するプロジェクトにあたってのバックエンド開発について語りたいと思います。

新しいホーム画面はまだ一部のお客さまにしかリリースされておらず、現在細かい調整や分析を行っている段階です。 「自分の端末だと昔からホーム画面は変わってないぞ?」という方は、申し訳ありませんがもうしばらくお待ちください。

メルカリの旧ホーム画面の課題

旧ホーム画面は下図のようなものです。カテゴリ一覧のタブを上部に備えており、各タブごとに商品がずらりと並ぶタイムラインによって主に構成されています。

f:id:akkie30:20190908154257p:plain
図1. 旧来のホーム画面

この画面にもいくつかのパーソナライゼーションは行われているのですが、十分ではありません。 ホーム画面はすべてのお客さまがアプリを起動するたびにほぼ必ず目にする重要な画面でありながら、カテゴリごとの新しい商品が並んでいるだけでそれぞれのお客さまの趣向に最適化された情報を提供するには新しい仕組みが必要となります。

雑多な商品の中から「掘り出し物」を探し当てる楽しさももちろんメルカリアプリの大切な要素ではあるのですが、お客さまの欲しいものをより短い時間で見つける手助けをするという目的で、より個人に最適化された情報の提供ができるホーム画面に作り直そうという話が出ました。

新しいホーム画面のデザイン

新しいホーム画面は、「いいね!した商品コンポネント」「新着商品コンポネント」など、各種情報をモジュール化して表示するデザインに決まりました。 下図のようなものです。画面上の各ブロックを「コンポネント」と呼んでいます。

これはタイムライン以外の情報をホーム画面に載せることを想定していたり、将来的にお客さまごとにホーム画面をカスタマイズしやすい状況を作ることを狙ったものです。 また、エンジニアの手を借りずともプロダクトマネージャーが表示するコンポネントやその順番を切り替えるABテストが簡単に行えるという未来像も見据えたものでした。同じ「いいね!した商品」の見せ方でも、横スクロールで次々に商品が現れる「カルーセル」か、それとも格子状に商品を表示する「グリッド」か、といった細かいレイアウトを切り替えるテストも個別に行いやすくするといったこともあります。

f:id:akkie30:20190908223117p:plain
図2. メルカリの新しいホーム画面

Homeマイクロサービス

記事を書きながら台風15号で雨風がえらいことになってきました。日曜日に千葉の温泉宿に泊まって、月曜日の朝に六本木に出勤しようと適当なスケジュールを立てて日曜の朝から旅行にでかけましたが、読みが甘かったかもしれません。

さて、メルカリのバックエンドでは現在PHPで書かれたモノリシックなアプリからGoで書かれたマイクロサービスへの移行を進めています。 その中で始まったこのホーム画面刷新プロジェクトも、やはりPHPではなく新しいサービスに実装しようということで「Home マイクロサービス」というGo言語で実装されるマイクロサービスを新しく作りました。Kubernetes、DataDog, Terraformなどインフラはすべて他のメルカリのマイクロサービスと同じ構成です。

Homeマイクロサービスの基本的な役目は「ホーム画面に必要な情報を他の依存サービスから集めて、コンポネントの配列の形でクライアントに返す」こと。IOS/Androidクライアントはその情報をもとに上記図2のようなUIを描画することになります。

コンポネントのデータモデル

さて、このデザインを実現するための各コンポネント(いいね!した商品、新着商品、など)のデータモデルは以下のようなものでした。

説明
ID 各コンポネントの識別子 1
Title コンポネントの上部に表示する文字列 "いいね!した商品", "新着商品"
Layout UI上に表示するレイアウトの識別子 Item_Carousel, Item_Grid_M, etc.
DataType コンポネントのPayloadのデータ型の識別子 Item_List_1
Payload 各コンポネントの実データ {[ {"itemId": "xxx", "price": yyy,...}, {"itemId": "zzz", "price": aaa,...}, ..., ]}

お客さまごとに個人最適化されたホーム画面を提供するためには、各コンポーネントのレイアウトや実データをすべてサーバ側で管理し、クライアントはサーバから返却されたものをただ表示するだけ、という状態を実現する必要があります。 そのために上記のようなマスタデータをサーバ側で持つことにしました。 将来的にコンポネントに表示すべきデータをバージョンアップしたいときには、DataTypeを新しく作成することで対応することにしました。

gRPCのAPIを提供するサービスであるため、これらのデータモデルもprotobuf上に定義しています。

API仕様

温泉宿で早く寝て今起きたのですが、台風15号の影響で宿が停電しています。その影響もあって内房線もすべて止まっており、今日六本木に戻ることは難しそうです。 会社にはリモートワークすることを伝えて、今日は残ったmacbookの電源とテザリングを使ってこの宿から働くことにします。読みが甘かった。電気も電車もない山奥でブログを書くことになるとは。しかし柔軟な働き方をサポートしてくれるメルカリのおかげで心配はありません。

さて、Homeマイクロサービスの基本的な役目はホーム画面に出すべき情報を依存する内部サービスからかき集めてクライアントに返すことです。 こうしたデータをアグリゲートするサービスでまず心配されるのは、依存するサービスに関連する大量のビジネスロジックが詰め込まれてしまい、実装が汚くパフォーマンスの上げにくいAPIが出来上がってしまうことです。

ホーム画面用のコンポネントの一覧を取ってくるAPIなら"GetComponents"という名前で1APIで作ってしまえばよいと考えるところなのですが、少しでもビジネスロジックが1APIに集中してしまう構造を避けるために2つのAPIに分けることにしました。

  • Get Layout API : 該当お客さまのホーム画面に表示されるコンポネントの順序・レイアウトだけを返却する
  • Get Components API: 指定された識別子のコンポネントの実データ(商品情報など)を取得する

それぞれのAPIは他のメルカリのマイクロサービスと同じくgRPCで作られており、全く新しいホーム画面用のAPIであるため旧来のホーム画面用のAPIと互換性を気にする必要などもありませんでした。

表示すべきコンポネントの識別子・レイアウトのみを取得するメリット

IOS/Adnroidクライアントは高速なGetLayout APIをまず叩き、UIの描画を開始するのに必要な情報を取得します。 ホーム画面に表示すべきコンポネントのレイアウト(カルーセル、中サイズのグリッド、など)の一覧さえ分かってしまえばUIを固定できるので、時間がかかることもある商品情報の取得はその後画面を乱すことなく非同期で行えます。

その後にGet Components APIを使用することで、指定されたコンポネントのデータを実際に取りに行くことになります。 往々にしてこの処理は時間がかかることが多いのですが、指定されたコンポネントをgoroutineやsingleflightを使用して必要最小限のデータを並列に取得するように努めました。

f:id:akkie30:20190909233004p:plain
図3. クライアントからHomeサービスへのリクエスト

GetLayout APIを作ったメリットは、

  • 「バグを発見したので特定のコンポネントを一旦非表示にしたい」
  • 「一時的に特定のコンポネントのレイアウトをカルーセルに変えたい」

といったプロダクトマネージャーからの急な仕様変更に応じるときにも実感しました。 Get Layout APIは単純な作りであるため、ほぼハードコーディングされているコンポネントのメタデータを書き換えるだけで上記のようなリクエストに答えることができ、複雑なGetComponents APIに手を出さずにスムーズにUIの変更をテストすることができました。

リリースしてみて

とはいうものの、メルカリの新しいホーム画面は2019年9月現在まだごく一部のお客さまにしかリリースされておらず、これから調整を繰り返していく段階です。パフォーマンス不足もあるため、まだ現状で旧来のホーム画面より改善したと言い切れるものではありません。

しかし大事なことは「単純な商品の羅列でしかなかったホーム画面に、今後最適化を行っていく基盤となる「コンポネント」という仕組みができた」ということです。ここから個人最適化やパフォーマンス改善を行い、より良いホーム画面を作ることができるように努力したいと思います。ここからが本当の勝負というところですね。

ちなみに:開発チーム

バックエンド開発は4人で行ったのですが、私以外は外国籍のメンバーで、開発に関わるコミュニケーションはすべて英語で行われます。 しかも4人が4人とも違う国籍であり意見も多様で、慣れないスクラム開発を導入しつつの開発でかつ全員がメルカリ歴の浅いメンバーであったため、当初は非常に難航したプロジェクトでした。 しかし、急速なグローバル化を進めているメルカリならではの環境で、楽しく開発をすすめることができたように思います。個人的な話ですが、このプロジェクト中に自分の英語力もグッと上がったように感じます。 "Work on Home" はチーム内では「家で働く」ではなく「Homeマイクロサービスの開発をしている」という意味だったりしました。

まとめ

本記事ではメルカリの新しいホーム画面を支えるHomeマイクロサービスについて、その背景を踏まえて概説させていただきました。

記事を書いている最中に台風15号に見舞われ、停電中で電気のつかない千葉県の温泉宿の暗い部屋の中で記事を書いていたりしましたが無事書き終えました。 この記事を書き終えたあとに決死の思いで山を出てバスとタクシーを乗り継ぎ、5時間以上かけてなんとか東京に帰ってくることができました。よかった。まだ停電中の地域に居る方々の無事と安全を祈りたいと思います。

さて、明日の記事は Kengo (@karolis_ml) さんによる「Multimodal Information Fusion for Prohibited Items Detection」の予定です。

この記事は Mercari Bold Challenge Month の13日目として @akkie30 からお送りしました。