こんにちは。モノタロウのTechBlog編集チームです。
モノタロウではECサイトでのお客様体験の向上を目指して、日々改善に取り組んでいます。 商品の出荷目安などの出荷関連情報は重要な要素の1つになります。
今回は、出荷関連情報の正確性を改善するとともにシステムの変更容易性を向上させるためにマイクロサービス化に取り組んだ活動をインタビューしました。
自己紹介
山崎 章裕
2019年8月に入社し、主にECサイトの注文・配送周りのプロジェクトにテックリードとして関わる。またECサイト開発の開発者体験の改善なども推進している。直近1〜2年はシステムモダナイゼーションの取り組みとAVLの開発を推進。
趣味はボルダリング。
藤本 洋一 楽天、SaaSベンチャーを経て、モノタロウに入社してマイクロサービス化にとりくむエンジニアの話
2019年5月入社。商品検索基盤のマイクロサービスとデータ生成に携わり、その後モダナイゼーションのファクトとなる横断的なシステムの分析、技術決定プロセスの策定を行う。AVLでは主に実装におけるアーキテクチャを担当する。
最近のイベント登壇記事:
・商品点数1900万点――「モノタロウ」の強みを伸ばす、コンテナ、Kubernetes活用の裏側:ビジネス成長とともに「複雑性」に立ち向かう - @IT
・MonotaROのクラウドネイティブ戦略 - ドメイン分割とマイクロサービス化でビジネスを支援
伊藤 泰
2023年3月入社。4月から11月までAVLチームでAVLの実装を担当し、12月からは受注ドメインのモダナイゼーションを主務とする。
守屋 綾祐
2019年新卒入社。OMS導入プロジェクトでパッケージとの連携システムの開発、CI/CD周りの新規導入を行う。AVLでは主にドメインロジックとテストの実装を担当。
納期表示を高度化する
ー 今回マイクロサービス化したサービスはAVLと呼ばれていますね。まずAVLとは何でしょうか?
(山崎) 米国で同じ事業を展開する兄弟会社であるZOROと定期的にディスカッションをしていて、向こうが進めていた在庫状況表示の仕組みを改善するシステムがAVL(Availabilityの略称)と呼ばれていました。
ー 大前提として、ECでは納期の情報は非常に大事ですよね。このAVLをモノタロウでも取り組もうとした理由は何ですか?
(山崎) 全社的にサプライチェーンの進化に優先度高く取り組んでいるのですが、システム面で関連するロジックが複雑で改善が困難になってきていました。そこでその部分をマイクロサービス化して改善することとなり、サービスのわかりやすい名前としてZOROで使われているAVLという名前を使わせてもらうことにしました。
(藤本) 既存システムは機能が多く依存関係も複雑で、ドメインがオーナーシップを持って高度化することが困難になっていました。納期はマイクロサービス化して、全体としては中尾さんの記事に書かれているようにモダナイズを進めています。
ー 納期表示の高度化とはどういったことを指すのでしょうか?
(山崎) モノタロウでは自社在庫を含む様々なパターンの出荷があり、同時にサプライヤ在庫連携が進んでいました。
ー それは 取扱点数1800万点以上、年20%成長を支えるMonotaROサプライチェーン!データドリブンに進化し続ける挑戦 のことですね。
(山崎) はい。サイトのフロントからそれらの正確な情報を伝える上で、在庫や配送のドメイン知識が欠かせなくなってきました。
(守屋) 引当・配送ドメインの知見を生かして、まず既存のAPIを改修するところにSQLやテストケースを提供しました。
ー まず既存のAPIに改修を行った上で、マイクロサービス化したわけですね。
(藤本) その上で、ストラングラーパターンで移行することを想定しました。
(伊藤) そして、マイクロサービス実装を始めるにあたり、protobufを用いたスキーマファーストによる API開発をGoで実装することを提案しました。
ー PythonやJavaは検討されなかったのでしょうか?
(藤本) 社内に言語とフレームワーク選定のチェックリストがあり、レビューを受けてADR化されます。今回は主に伊藤さんにチェックリストを記入していただきました。
(伊藤) 私は前職でGoとgRPCによるマイクロサービスを経験しており、protobufによる型安全性とスキーマファーストでの開発が重要であると確信していました。gRPC、connect-goの使用などはADRとして記載し、藤本さんをはじめとするチームメンバー全員からのレビュー/承諾を経て採用されています。
サプライヤ在庫連携機能開発のつらみ
(山崎) モノタロウのサイトでは至る所で商品情報が掲載され価格や出荷目安などの情報が表示されています。今回サプライヤ在庫連携の開発に関わりましたが、サイトの出荷目安・出荷ステータスの情報にそれを反映させるのがとても大変でした。
ー なにが大変だったんでしょうか?
(山崎) 出荷目安を計算する似て非なるロジックが何ヶ所かに存在し、どのロジックを変えるとどのページに影響するのかの調査が大変でした。またロジックの実装言語もPythonだったりBigQuery SQLだったりといくつかありました。そのプロジェクトは関係各所を巻き込みながらなんとかつらみを乗り越えてリリースできたのですが、在庫・出荷にまつわるロジックを1つのチームでコントロールできる状態にしておくことの重要性を痛感しましたし、そのためにはマイクロサービス化が必要と感じました。
AVLのマイクロサービス開発のすすめ方
ー それでは、具体的にどのようにマイクロサービス化を進めていったのかを教えてください。
(伊藤) AVLの実装初期に、Goによるマイクロサービス実装のテンプレートリポジトリ(GitHub ActionsによるCI/CD含む)や共通ライブラリを用意し使用してもらいました。主な理由は2点あり、一つ目はモノタロウ内にはドメインロジックをGoで実装した事例がなかったので、それを補助するためです。二つ目はCI/CDも含めて実装初期から整っていた方が、品質の良いものを効率的に作れるだろうと考えたからでした。また、テンプレートリポジトリにはDDD/クリーンアーキテクチャ的なサービス実装サンプルがあり、それも参考にしてもらえたらと考えていました。テンプレートリポジトリと共通ライブラリの提供をAVLの実装初期に間に合わせるため、最初に提供したものにはラフに作っている部分があり、AVLを実装しながらチームメンバーとともにブラッシュアップしていきました。
(藤本) Pythonで書かれた既存システムをリライトする上で何らかのデザインパターンを持ち込みたいと考えました。既存システムは長年建て増しを重ねたファットコントローラでした。それを単一責任の原則を満たすように分解するには、明確なデザインパターンに沿って進めるべきと考えました。そこで、まず既存システムのコードを分析したところ、複数のStrategyからなるBuilderパターンが仮説として浮かび上がりました。
ー 先ほどのサプライヤ在庫連携のようなところはStrategyに向いていそうです。
(藤本) 実装する上で、GoにはいろいろなORMがありますが、その中でsqlcを選択しました。APIのリビルドということで、既存のスキーマやクエリを大きく変えられないためです。正直MySQLでsqlcを使った国内事例は多くはなく、期待通りではない部分もありましたが、チーム内でノウハウを共有しながら実装を進めました。
(守屋) sqlcを使用しDBアクセスをするようになった時点で、IntegrationTestと、ストラングラーパターンでの移行を見据えた既存システムとの比較テスト、の2つのテストを自動化しました。 IntegrationTestは一般的なDocker composeを用いたものでmysql, api, apiにリクエストを送信するクライアントのコンテナをそれぞれ立ててレスポンスが期待値通りかテストするものです。
(守屋) 比較テストはステージング環境の既存システムとAVL両方に対し同じリクエストを投げて結果を比較するものです。比較テストで差異のあるものを修正し、IntegrationTestのケースとして実装することでデグレを防ぐことができ、品質向上に寄与できたと思います。
(藤本) IntegrationTestが動くようになったあたりから、チーム内でDatadog APMのtracingを見てチューニングが進むようになりました。最初は品番ごとにgoroutineを分けていたためにN+1問題に悩まされていましたが、Safari onlineで参照した実践ハイパフォーマンスMySQL 第3版を参考になるべくPKをINでselectするようにしたことで、ほぼ品番数に比例したレスポンスタイムが実現できました。
ー とても面白いですね!今回やったことをより詳細に知ることはできるのでしょうか?
(藤本) 今後、テックブログでも個別の取り組みについて詳しく解説した記事を出していく予定です。
リリース・監視・その後の展開
ー CI/CDや開発サイクルはどうだったのでしょうか?
(伊藤) モノタロウではGitHub Enterprise CloudとEKSクラスタを活用しており、AVL及びテンプレートリポジトリのGitHub Actionsにより、イメージがECRにpushされEKSクラスタにArgoCDでデプロイされます。
ー GHEはこちらに導入の記事が出ていましたね。EKSの記事もありました。
(伊藤) AVLの開発と並行してマイクロサービスの社内展開に向けてprotobufと共通ライブラリの共有リポジトリを作りました。protobufのリポジトリにprotoファイルを登録するとサーバーとクライアントのスタブライブラリをGo/新しいPython/古いPython用のパッケージが自動的に生成されるようにしました。
(藤本) 複数のリポジトリ間の連携には当初PATを利用していましたが、GitHub Appsに切り替えて有効期限の問題がなくなりました。
(山崎) 本番リリースは平均週2回行っていて、dependabotを含む変更が小さいうちにリリースしています。リリース手順はActionsとArgoCDでほぼ自動化できていますが、まだ改善の余地があると考えています。
(伊藤) いわゆる Observability(分散トレース、ログ、テレメトリ)の機能が共通ライブラリには入っており、OpenTelemetryのSDK経由で分散トレースとテレメトリがDatadogに送られるようになっています。加えて、サービスから出力されるログには自動的に分散トレースとの紐付け情報が埋め込まれるようになっていたり、エラー時にはSentryにレポートするようになってます。これらにより開発者自身での運用も可能にはなっています。ただし、Datadogのダッシュボード/モニターや弊社ログ基盤の設定など様々な設定は自動化/セルフサービス化されていないので、その辺の設定の自動化/省力化にも現在取り組んでいます。
(藤本) これまで出てきたような技術的な決定とトレードオフはADRにまとめられており、今後プロジェクトに参加する人にも背景がわかるようにしています。モダナイゼーションを組織に展開するために必要な取り組みだと考えています。
(山崎) 社内にレガシーなシステムが多い中で、AVLという先行事例ができたので、このような開発を広げていきたいと考えています。
おわりに
今回は、納期表示の高度化を実現するための仕組みを、どうやってマイクロサービス化してきたかの概要をお話しいただきました。
今後は、テンプレートライブラリ、デザインパターン、コンテナ推進などの個別の取り組みについても本テックブログで紹介する予定です。