Slide 1

Slide 1 text

生鮮ECプラットフォームの バックエンドを支えるRails クックパッド株式会社 買物事業部部長 勝間 亮

Slide 2

Slide 2 text

•勝間 亮 (かつま りょう) @ryo_katsuma •2009~ クックパッド入社 ‣レシピ領域バックエンドエンジニア ‣レシピ領域マネージャー •2018~ 買物事業領域立ち上げメンバー ‣副部長 兼テックリード ‣2020~ 部長

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

クックパッドマート •2018年〜買物領域の新規事業 •生鮮食材のEC ‣ 半分正しい、半分正しくない

Slide 5

Slide 5 text

クックパッドマート •インターネットで生鮮食材を扱う上での課題 1. 生鮮食材の流通の課題 2. 生鮮ECの課題

Slide 6

Slide 6 text

生鮮食材の流通の課題

Slide 7

Slide 7 text

これまでの流通網 生産・出荷組合 ¥120 市場・直売所 ¥130 仲卸・問屋 ¥150 小売店 ¥150〜198 生産者 ¥50~80 • お届けまで数日〜数週間 • 中間業者が通るたびに利益が乗る構造 • 生産者の手取りは小売価格に比べて少ない

Slide 8

Slide 8 text

解決アプローチ クックパッド マート ¥120〜150 生産者 ¥80~100 •当日集荷、当日配送 • 生産者直売で地域で一番安く買える(= 生産者の手取りを増やせる) • 生産者直売でお届けまでのリードタイムが短い(= 新鮮なままお届け)

Slide 9

Slide 9 text

生鮮ECの課題

Slide 10

Slide 10 text

現状の生鮮EC •個配の配送コスト問題 ‣ 多くのECサービスは最低注文金額3,000~5,000円 ‣ まとめ買いをせざるをえない •再配達問題 ‣ 肉・魚などナマモノは宅配Boxや置き配を適用しづらい ‣ お届け時間に必ず家にいないといけない制約

Slide 11

Slide 11 text

解決アプローチ •敢えて個配をしないピックアップ型EC ‣ 生活動線上に受取り場所を作って自分で好きな時間で取りに行く ‣ 再配達問題を解決 •集荷配送コストの圧縮 ‣ 複数人の注文をまとめて配送することで配送コストを1/N ‣ 生産者の集荷場所も一元化することで集荷コストも1/M ‣ 最低注文金額を0円に

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

クックパッドマート •生鮮ECサービス •生鮮ECプラットフォーム

Slide 15

Slide 15 text

クックパッドマート •生鮮ECサービス •生鮮ECプラットフォーム

Slide 16

Slide 16 text

クックパッドマートの バックエンド

Slide 17

Slide 17 text

バックエンド •Rails 6.0.2 •Ruby 2.6

Slide 18

Slide 18 text

可能なかぎり素朴なRailsに •MVC以上のレイヤーは極力増やさない ‣ app/services • 複数Modelをまたがる処理 ‣ app/resources • RESTful APIレスポンスのClassを定義するModelのラッパー

Slide 19

Slide 19 text

モノリシックなサービス ‣ ユーザー向けモバイルアプリAPI ‣ 販売者向けWebアプリ ‣ ドライバー向けモバイルアプリAPI ‣ スタッフ向け管理Webアプリ ‣ ハードウェアキッティング会社向け管理Webアプリ

Slide 20

Slide 20 text

モノリシックなサービス •新規事業なので変化がかなり大きい ‣ サービスをまたいだ実装を単純化 ‣ アプリ分割によるModel層の管理の煩雑化を避けたい (DBは共通) •必要になればサービス分割を検討 •必要な外部サービスは適宜利用 ‣ 決済: Stripe / Push通知: Firebase

Slide 21

Slide 21 text

実装も素朴 •の、ように見える

Slide 22

Slide 22 text

実装も素朴? •の、ように見えるかもしれないが。。。

Slide 23

Slide 23 text

現実世界を反映した 設計と開発

Slide 24

Slide 24 text

物体と概念のmodel設計

Slide 25

Slide 25 text

受け取り場所(マートステーション)

Slide 26

Slide 26 text

モデル設計 • ステーションは複数の冷蔵庫を • 冷蔵庫は複数のコンテナを • コンテナは複数の注文商品を • 注文商品は商品にひも付き

Slide 27

Slide 27 text

モデル設計 • Location has_many LocationFridges • LocationFridge has_many LocationFridgeContainers • LocationFridgeContainer has_many OrderItems • OrderItem belongs_to Item

Slide 28

Slide 28 text

モデル設計 • Location has_many LocationFridges • LocationFridge has_many LocationFridgeContainers • LocationFridgeContainer has_many OrderItems • OrderItem belongs_to Item

Slide 29

Slide 29 text

コンテナの内容は日毎に変わる 「ある日のコンテナ状況」を再現するときにSQLだけで解決したい

Slide 30

Slide 30 text

モデル設計 • Location has_many LocationFridges • LocationFridge has_many LocationFridgeContainers • LocationFridgeContainer has_many DeliveryLocationFridgeContainers • DeliveryLocationFridgeContainer has_many OrderItems • OrderItem belongs_to Item 5/10付

Slide 31

Slide 31 text

モデル設計のポイント •現実世界のものをそのままモデリングしても駄目 •現実世界と結びつく「概念」をどう表現するか ‣ 例) モノは同一でも時系列で状況が変わるもの ‣ 表現をミスるとデータ取得が困難に

Slide 32

Slide 32 text

モデル設計のポイント •とはいえ何度も設計はミスってる •DB設計ミスに気づいたら積極的に リファクタリング ‣ 作り直しは許容 ‣ 2度目はさすがにうまくいく

Slide 33

Slide 33 text

物理制約の設計

Slide 34

Slide 34 text

一般的ECの購入時チェック •在庫が存在するかどうか •ユーザー情報が正しいかどうか ‣ 決済情報、住所など

Slide 35

Slide 35 text

集荷からお届けまで

Slide 36

Slide 36 text

集荷冷蔵庫 集荷からお届けまで

Slide 37

Slide 37 text

配送車 集荷からお届けまで

Slide 38

Slide 38 text

受け取り用冷蔵庫 集荷からお届けまで

Slide 39

Slide 39 text

注文処理ではこれらのチェックが必要

Slide 40

Slide 40 text

受け取り冷蔵庫のキャパシティ •注文商品はコンテナに入る必要 ‣ order_items.sum(&:volume) 
 ɹ<= delivery_location_fridge_container.available_capacityɹ ‣ を満たすコンテナがあるかを確認

Slide 41

Slide 41 text

商品毎の重さや体積測定

Slide 42

Slide 42 text

配送車のキャパシティ •ステーションのコンテナが全て車に入る必要 •配送資材(シッパー)にコンテナを入れて配送 ‣ delivery_location_fridge_containers.size 
 <= distribution_routing_shipper.available_capacity ‣ を満たす車内のシッパーがあるかを確認

Slide 43

Slide 43 text

集荷用冷蔵庫のキャパシティ •集荷用冷蔵庫にコンテナが入る必要 •配送コンテナを設置する空き棚があるか ‣ CollectionSpotFridgeAddress
 .where.not(id: already_assigned_address_ids)
 .merge(collection_spot_fridge: collection_spot_fridges) ‣ を満たす空き棚があるかを確認

Slide 44

Slide 44 text

採番処理 •注文された商品に ‣ どの配送先冷蔵庫のどのコンテナに入るか ‣ どの配送車のどの資材に入るか ‣ どの集荷元冷蔵庫のどの棚に入るか •の情報(= nanika_id)を付加する採番処理を行う

Slide 45

Slide 45 text

物理制約の設計のポイント •制約の実装は複雑になりがち ‣ 現実世界を表現すると仕方なし(と思ってる) •制約を採番処理と見立ててServiceClassにまとめる ‣ DeliveryLocationFridgeContainerAssignerService ‣ DistributionRoutingShipperContainerAssignerService ‣ CollectionSpotFridgeAddressAssignerService •採番ができない場合は物理制約にひっかかったと見なしてraise

Slide 46

Slide 46 text

物理制約のValidation

Slide 47

Slide 47 text

一見、採番成功していても注意 •タイムセール施策 ‣ 継続率向上 •特定の商品への注文が短期間に集中 ‣ 同一データに対してread/writeが集中

Slide 48

Slide 48 text

実際にあった話 •注文時の採番処理は完了 •早朝にドライバーへの集荷指示データ作成時にraise •

Slide 49

Slide 49 text

実際にあった話 •注文時の採番処理は完了 •早朝にドライバーへの集荷指示データ作成時にraise •ドライバー稼働開始までにデバッグのタイムアタック

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

ここから9:00までにデータ不整合を直して ドライバーに指示データを作成しないと集荷が間に合わない

Slide 52

Slide 52 text

起きたこと •決済サービスへの通信も挟まり、transaction処理を 行いづらい •受け取り場所Aの注文データに対して 受け取り場所Aには巡らない配送車の資材IDが採番 ‣ データとしてはIDは採番されている ‣ 採番されるべきでないデータでコンテナが奪われる

Slide 53

Slide 53 text

要するにこうなっていた •DBのデータ的にはValidationはOK •物理世界を考慮したビジネスロジック的にはNG

Slide 54

Slide 54 text

対応方針 •定期データチェックバッチ •DBのデータが ビジネスロジック的に正しいか ‣ 未来の全注文データを定期チェック ‣ 最悪raiseしても落ち着いて対応

Slide 55

Slide 55 text

まとめ

Slide 56

Slide 56 text

まとめ •流通を愚直に表現すると ‣ 物理と概念を的確に紐付けた設計する必要 ‣ 物理世界の制約を表現する必要 ‣ ビジネスロジックを満たすデータかチェックする必要

Slide 57

Slide 57 text

まとめ •愚直に実現することは(ご覧の通り)複雑 •が、それはそれで面白い ‣ 普通のRailsアプリではない技術的な楽しさ

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

https://www.wantedly.com/projects/300736

Slide 60

Slide 60 text

ご清聴ありがとうございました