クックパッドマートのドライバー向けWebアプリケーション

クックパッドマートの開発に携わっていますバックエンドエンジニアの中村です。

クックパッドマートは生鮮食品のECサービスで、流通の仕組みを自分たちで作っています。当然ですが商品を流通させるには、物理世界でものを動かす必要があります。実際にものを運ぶのはドライバーと呼ばれる人が行っており、このドライバーに向けていつ・何を・どこに運ぶといった指示をする必要があります。このエントリではそんなドライバーへの指示のために開発しているWebアプリケーションを紹介します。

各便のアプリケーション

クックパッドマートの流通の中には販売者が出荷してからユーザーの手元に届くまでに、タイムライン別に複数の流通方法が存在しています。タイムライン順に 「出荷サポート便」,「ハブ便」,「ステーション便」 といった流通方法が存在しておりそれぞれの便で個別にWebアプリケーションを用意しています。各流通方法を含む流通全体についてはこちらのエントリを見ていただけるとわかりやすいかと思います。

出荷サポート便Web

ローカルスポットという販売者の一時出荷拠点からハブという最大の流通拠点に運ぶ際に使用します。ドライバーはその日立ち寄るローカルスポットを確認し、各ローカルスポットで指定された商品を集荷します。その後指定されたハブの番地に商品を納品します。

ハブ便Web

あるハブから別のハブに運ぶ際に使用します。ドライバーは立ち寄るハブを確認し、それぞれのハブで指示されたコンテナを集荷・納品します。また集荷の際には行き先を示したラベルに張り替えるため、コンテナ用のラベルの印刷を行います。

ステーション便Web

ハブからステーションというユーザーが実際に受け取りを行う拠点に運ぶ際に使用します。ドライバーは集荷するハブを確認し、ハブでコンテナを集荷します。集荷の際には納品先のステーション番地のラベルに張り替えるため、コンテナ用のラベル印刷を行います。その後指定されたステーションに立ち寄りながらコンテナを納品していきます。

使用技術

開発時期によって違いはありますが、最近は以下のような構成にしています。

いわゆるBFF(Backend For Frontend)は配置せず、クライアントから直接APIを呼んでいます。パブリックに公開しているサービスではないのでSEO対策が不要なこととそこまでシビアなパフォーマンスを求められるわけではないので基本的にはSSR(Server Side Rendering)を使用せずシンプルな構成にしています。 SSG(Static Site Generator)も検討しましたが、一部動的に処理したいものがあったためバックエンドと同じく社内基盤のAWSのECS上にデプロイしています。

MUIを活用

ドライバー向けという性質上、パブリックに公開しているサービスに比べてリッチな独自デザインの必要性は低いため、流通チームには専属のデザイナーがいません。 そこで、MUIをほぼデフォルトで使いきることでドライバーの使用に問題ないクオリティのUIを素早く実装できるようにしています。

ネイティブアプリからの移行

実は元々Android/iOSのネイティブアプリを提供していた時期もありました。しかしチーム内にはサーバーサイドのエンジニアしかおらず、アプリを改修したいとなると他のチームに依頼をしてリソースを調整するといったことが都度発生し、どうしても足が遅くなってしまっていました。日々流通の形が進化していくので、こうした足の遅いアプリの開発がボトルネックになってしまうことが多くなってきました。しかし、一方で常にアプリ開発のタスクがあるわけではなく散発的に改修が必要になる状況で、流通チーム内にアプリエンジニアを常駐させておくほどではありませんでした。それならいっそ全てWebに移行して、サーバーエンジニアだけで開発を完結できるようにしようという決断をしてネイティブアプリからWebアプリにリプレイスしました。

Next.jsを選択したのもネイティブアプリで提供していたUI/UXに遜色ないものを実現したかったというモチベーションが大きかったからです。Reactベースのフレームワークは他にも選択肢がありますが社内でも実績・ノウハウのあるNext.jsを選びました。

ネイティブ特有の機能

リプレイスの際、大きなハードルだったのはネイティブアプリ特有の機能をどうするかということです。幸いにも最近のブラウザはかなりの機能を実現できるようになってきており、絶対にネイティブアプリでなければならないということが少なくなってきました。

カメラ

集荷・納品時の検品のためにQRコードを読んだり、納品結果の撮影のために使用しています。

一番苦労したのはカメラの権限の許可設定でした。対象のドメインとブラウザ自体の2箇所で権限を許可する必要があり、全ドライバーに権限を許可してもらうよう展開するのが開発より大変でした。

プッシュ通知

プッシュ通知も実現できるようになりつつありますが、iOSは未サポートだったりと全端末で活用できるわけではありません。代わりに全ドライバーをSlackに招待してSlack上でメンションすることによって通知するようにしました。

その他にもSMSを送信することも検討していて、実現の目処も立ってはいたのですが、別文脈でドライバーをSlackに招待してコミュニケーションをとるようにする取り組みがあり、機能としてもかかる工数としても都合がよかったため利用した形です。

位置情報

使っていません。ネイティブアプリでは取得していたのですが、結局活用しなかったのでリプレイス時にはオミットしました。仮に使いたくなった場合はドライバーの位置情報から配送の進捗を見たいといった用途が考えられるので、Webだとバックグラウンドで取得できないことが高いハードルになりそうです。

最後に

マートのドライバー向けWebアプリケーションについて紹介してきました。

途中にも書きましたが日々流通の形が進化しており、この先もまだまだ改善していく必要があります。単なるWebアプリケーションではなく物理世界と密接に関係している開発ができるのはなかなか刺激的で楽しいので、少しでも面白そうだなと思って頂いた方はぜひご連絡ください。

2022年クックパッドマート連載の他のエントリ

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/