実践クリーンアーキテクチャ - 複雑化した大規模ECサイトをモダナイズしたモノタロウの事例
クリーンアーキテクチャのメリットとは?またいかにして導入するか?難解なイメージのあるクリーンアーキテクチャの概要を採用事例に学びます。今回、取材したのは工業用間接資材オンラインストアの「モノタロウ」。サービスの開発を続けていくにつれ、同社のシステムは複雑化、肥大化していき、様々な課題が生じたそうです。こうした課題に対応すべく、システムのモダナイゼーションに取り組む際、取り入れたのは、クリーンアーキテクチャでした。同アーキテクチャをどのように実装したのか、モノタロウのエンジニア3人に聞きました。
- 受け入れテストを自動化し、システムの正常動作を保証
- ユニットテスト導入の秘訣は「テストを書くハードルを下げる」こと
- クリーンアーキテクチャ化は、“幹”の処理から手をつける
- クリーンアーキテクチャを全社的に展開するために
- BigQueryを活用してログ情報を一元管理
- スケーラビリティの高い新EC基盤を構築
- システムを、ビジネスのボトルネックにしてはいけない
「レガシー化したシステムをどうやってメンテナンスし続けるか?」は、サービスを長年運営してきた企業の多くが直面するテーマです。15年以上の歴史を持つ、事業者向け工業用間接資材オンラインストア「モノタロウ」も、このテーマと向き合ってきました。
サービスの開発を続けていくにつれ「システム構成が複雑になり、リリースや障害箇所の特定に時間がかかる」「自動テストが書かれておらず、ソフトウェアが大きくなるにつれ品質を保つのが難しくなる」などの課題が徐々に表面化してきたのです。
この課題を解決するため、サービスの開発者たちは数年前から、システムのモダナイゼーションに取り組んでいます。今回は彼らに、長い歴史を持つシステムを刷新するノウハウを解説していただきました。
- 芝本雅史さん(しばもと・まさし/@silver_birder/Silver-birder)(写真右)
- Web技術の関心が強いエンジニア。新卒でSE企業に就職し、Webアプリケーション開発の上流~下流工程を経験後、2018年にMonotaROへ入社。主に、Webアプリケーションのシステムリプレースに従事している。モノリスで密結合したWebアプリケーションに対して、様々なアプローチを用いて疎結合なものへと再構築中。著書に『はじめてのWebComponents入門』がある。ポートフォリオサイトはこちら。
受け入れテストを自動化し、システムの正常動作を保証
──「モノタロウ」は15年以上も前に生まれたEコマースサイトだそうですが、プログラミング言語やフレームワークは何を使用していますか?
金谷 プログラミング言語はPythonを使用しています。用いているフレームワークは、当社の独自実装のものです。「モノタロウ」の開発が立ち上がったばかりの頃は、デファクトスタンダードといえるPythonのWebフレームワークが、まだ世の中に存在していなかったためです。
──独自実装のフレームワークですと、メンテナンスが大変そうですね。
金谷 そうです。メジャーなフレームワークの場合、何か問題が発生したときでもWebや書籍の情報をもとに解決できますが、独自実装のフレームワークではそれができません。機能追加や不具合調査に時間がかかっていました。
さらに、システムのメンテナンスが続けられていくなかで、設計が複雑になり、ソースコードのコピー&ペーストも多くなっていました。新しいメンバーが参画した際の、システムの学習コストも高くなっていたのです。他にも、ユニットテストや受け入れテストのテストコードが全く書かれていないという課題もあり、手動テストに多くの時間を割く必要がありました。
──大変な状況ですね……。その課題を解決するため、システムのモダナイゼーションを始めたのですか?
金谷 はい。私は2016年頃から運用改善に取り組んでおり、改善初期はリリースの自動化やデプロイパイプラインの構築を行いました。その際に、「自動デプロイされたシステムが正常に動くかどうかを、end-to-endでテストしたい」というニーズが出てきたのです。そこで、受け入れテストの自動化をスタートしました。
──受け入れテスト自動化のために、何に取り組みましたか?
金谷 テスト自動化フレームワークである「Robot Framework」を導入しました。これは、当社のPythonスペシャリストである増田泰というエンジニアが薦めてくれたものです。
「Robot Framework」では、自然言語を用いてテストケースを記述することができます。シナリオと画面固有の操作を分離する「Page Object Pattern」というデザインパターンに合わせて、シナリオと画面固有の操作を明確に分けるように早い段階で設計しました。
シナリオにはテストしたい動きを自然言語で記述し、自然言語と紐付く具体的な動作を画面固有の操作をまとめたファイルに記述しました。画面の修正に応じて画面固有の操作を修正することはありますが、シナリオにまで影響することはありません。「Robot Framework」自体の使いやすさと相まって、非常にメンテナンスしやすいです。
「Robot Framework」がPython製なのも当社にはマッチしていました。「モノタロウ」の開発をPythonで行っているため、メンバーの端末には必ずPythonがインストールされているからです。
──「Robot Framework」の導入により、どのような変化が生じましたか?
芝本 リリース時の安心感が段違いになりました。「モノタロウ」はシステムの規模が大きいので、全機能を人の手だけでテストすることは不可能です。ですが「Robot Framework」でテストを書いておくことで「ユーザーがよく使う操作は、正しく動くことを自動テストが保証する」という状態を保てるようになりました。
ユニットテスト導入の秘訣は「テストを書くハードルを下げる」こと
──受け入れテスト自動化の後は、何に取り組みましたか?
金谷 開発のより早い段階で機能をテストできるように「ユニットテストのテストコードを書こう」という流れになりました。
──メンバーにテストコードを書いてもらうため、どのような工夫をしましたか?
金谷 「テストを書くためのハードルを極力下げる」ことに注力しました。例えば、当社は一部のエンジニアがWindows端末で開発をしていますが、ユニットテストに必要なPythonライブラリのなかには、Windows上ではうまく動作しないものがあります。
その課題を解決するため、コンテナを用いて環境構築をし、コンテナ上でユニットテストを動かす方針にしました。もう1つのハードルになっていたのが「メンバーがユニットテストを書くことに慣れておらず、どのように手をつけたらいいのかわからない」という課題でした。
──その課題を、どうやって解決しましたか?
金谷 テスト駆動開発(Test Driven Development)について実習形式で学ぶ、社内向け勉強会を実施しました。サンプルアプリケーションをテスト駆動開発のお作法1に則って開発してもらうことで、「ユニットテストはこうやって書けばいい」という感覚をメンバーに掴んでもらったのです。
おかげで、テスト駆動開発に取り組む人が増え、「モノタロウ」のユニットテストは徐々に増加していきました。現在(2019年11月の取材時点)では、約2,500ケースほどの量になっています。
──かなり増加しましたね。ユニットテストの導入により、開発フローそのものも改善されたのでは?
芝本 ソフトウェアの品質が向上したのはもちろんですが、ユニットテストがあることで、ソースコードレビューの際に「開発者はどんな動作を想定してコードを修正したのか」が明確になりました。Pull Requestの説明文だけではなく、テストコードの内容からも修正の意図が読み取れるようになったからです。ユニットテストを書く文化が生まれたことで、レビューする側の負担も軽減されました。
クリーンアーキテクチャ化は、“幹”の処理から手をつける
──他にはどのようなシステム改善を行いましたか?
芝本 私たちのチームでは、システムのクリーンアーキテクチャ化を実施しました。最初に手をつけたのは検索ページです。
──クリーンアーキテクチャを選択したのはなぜでしょうか?
芝本 各処理を疎結合にし、独自実装のフレームワークに依存している処理を、他のフレームワークに代替可能な状態にしたかったためです。クリーンアーキテクチャは依存の方向性が単一方向であり双方向依存がありません。フレームワーク固有の処理などレイヤーの外側に存在する要素は、置き換えがしやすくなります。
それから私たちのチームでも、過去にはユニットテストがほとんど書かれてこなかったため、今後はなるべくテストコードを増やしていきたい。ですが、旧来のアーキテクチャでは、モックを差し込むことが困難でした。
クリーンアーキテクチャを採用すれば、その課題も解決しやすくなります。各レイヤーが抽象に対してのみ依存しているため、モックを挟みこむのが容易だからです。
──「モノタロウ」の検索ページは、それなりに規模の大きなシステムかと思います。どのようにして、改修を進めていったのですか?
芝本 機能のなかでも“幹”といえる処理の設計から着手していきました。
──“幹”とはどういうことでしょうか?
芝本 検索ページのなかには、キーワード検索はもちろん、商品カテゴリによる検索など多種多様な検索ページが存在します。そのうち、検索ページの根幹といえるのはキーワード検索です。この機能を“幹”と呼び、他の機能を“枝”と呼んでいました。前者の機能を、まずはクリーンアーキテクチャ化していくと決めました。
──どのような手順で、幹を設計していくのでしょうか?
芝本 設計を始めた段階では、いったんすべての処理をユースケース層に置いてしまいます。
その後、チーム内で議論し合いながら、それぞれの処理を適切な層へと移していくわけです。例えば、APIの呼び出しが発生するのならば、その処理をリポジトリ層に移譲します。
APIから取得したデータを加工する処理があれば、画面表示のための加工ならばプレゼンター層に、業務ロジックのための加工ならばドメイン層に置くというように「処理の目的に応じて、適切な層にふり分けていく」という形で設計を行いました。
幹の処理の設計方針が決まってからは、枝の処理にも同様の設計を適用していきました。
クリーンアーキテクチャを全社的に展開するために
──クリーンアーキテクチャ化のプロジェクトは現在も継続中ですか?
芝本 検索ページはすでにクリーンアーキテクチャ化が完了しましたが、「モノタロウ」には他にもたくさんのページがありますから、それらの変更にも順次手をつけています。とはいえ、少人数のメンバーだけで、(芝本氏の所属チームが担当する)すべてのページをクリーンアーキテクチャ化するのは無理があります。
そこで、外部の業務委託の方々の助けも借りる方針にしました。リポジトリ層やドメイン層といった層を外部のエンジニアに開発していただき、それらの層を自社のエンジニアがインタラクター層で組み立てるという役割分担をしています。分業を行いやすいのも、各層の役割が明確なクリーンアーキテクチャの利点です。
──「クリーンアーキテクチャを導入したい」と考えている読者の方々に、アドバイスはありますか?
芝本 クリーンアーキテクチャは層の種類が多い、いわば“重たい”設計でもあります。そのため、立ち上がったばかりのアプリケーションにクリーンアーキテクチャを用いるのは時期尚早です。ある程度の規模まで成長してから、クリーンアーキテクチャへの移行を検討してほしいです。
それから、アプリケーションの規模が大きいということは、移行プロジェクトも長期にわたるものになります。「クリーンアーキテクチャ化後に、どのようなテストをして品質を保証するか」「リリースをどのような手順で行うか」などの方針を予め決めておく方がいいです。
もうひとつ、クリーンアーキテクチャの設計において理想を追い求めすぎると、いくら時間があっても足りなくなってしまいます。できる限り、現実的な落としどころを模索してください。「直したい」と思う部分は、後でリファクタリングすればいいですから。
BigQueryを活用してログ情報を一元管理
──他に、アーキテクチャ変更を行った部分はありますか?
金谷 他のメンバーが、Google BigQueryを使ったログ基盤の構築を行いました。当社では昔から、ログ情報を用いてサービスの利用状況を分析していました。ですが、ログ抽出の方法にいくつもの課題がありました。
例えば、サービスによっては各サーバーにログインしてログを取得する必要があったり、MySQLに格納されているログ情報を取得する際、検索条件によってはレスポンスが返るのに時間がかかったりしていました。そこで、Google BigQueryを用いてログ情報を一元管理できる基盤を構築しました。
──新しいログ基盤の特徴について解説してください。
金谷 新しいログ基盤では、Fluentdを用いて各サーバーのログファイルをAmazon S3などにアップロードした後、AWSやGCPなどのマネージドサービスでログを加工して、Google BigQueryに保存します。
このログ基盤では、「ユーザーからのリクエストを横断的に追跡できること」を大事にしています。「モノタロウ」では、サービスにリクエストがあった際に、応答を返すまでにはいくつものサーバーを経由します。そのため調査を行う際には、複数のサーバーをまたぐ処理が、どのような経路を経たのかを追跡できる必要があります。
「モノタロウ」ではサービスの前段にAkamaiを使用しており、まずはAkamaiがリクエストを受け付けます。その時点でリクエストごとに固有のIDを生成し、後段のサーバーにID情報を引き継いでいくようなアーキテクチャ設計を行っています。
Nginxのアクセスログにも、アプリケーションログにも、さらに後段にあるSolrなどのログにも、Akamaiが付与したIDを表示させます。それらのログ情報がGoogle BigQueryに格納された後、IDをキーにして横断的に検索できるようになっているわけです。
スケーラビリティの高い新EC基盤を構築
──藤本さんは、どのような取り組みを行っていますか?
藤本 私たちのチームは、新しいEC基盤の構築に取り組んでいます。「モノタロウ」は非常に売上規模の大きなECサイトになってきました。その成長を支えていくには、システム基盤の整備が必要不可欠です。
例えば、Kubernetesを活用してスケーリングしやすいサービス基盤を構築したり、マイクロサービス化を推進したりと、さまざまな施策をスピーディーに回していけるようなプラットフォームを構築する必要があります。
──例えばどのような処理に、Kubernetesを導入するのでしょうか?
藤本 例を挙げますと、「モノタロウ」では1,800万種類以上もの商品データを扱っているのですが、これらのデータは商品管理システムや注文管理システムなどから連携されたものをバッチ処理によって加工しています。
データ量は多いですが、現在はバッチで処理しきれていますが、今後はサービスの成長とともにデータ量も増えますから、いずれ捌ききれなくなるでしょう。こうした課題を解決するには、Kubernetesを取り入れたスケールしやすいアーキテクチャが向いています。
──ビジネスのスケールのためには、スケールを前提としたアーキテクチャ構築も必要不可欠なのですね。
藤本 さらにいえば、新しいEC基盤を構築するには「スキルの高いエンジニアをいかにして採用するか」も考えていく必要があります。Kubernetesやマイクロサービスといった、エッジーな技術の知見を持ったメンバーを集める必要があるからです。
そのため、当社はこれまで尼崎のオフィスを主な開発拠点にしていましたが、2018年には新しく東京オフィスを新設し、東京でもエンジニアを採用できる体制を整えています。私は東京拠点のメンバーとリモートで連携を取り合いながら、プロジェクトを進行しています。
システムを、ビジネスのボトルネックにしてはいけない
──最後に伺いたいのですが、システムのモダナイゼーションに取り組まれたことは、「モノタロウ」の開発においてどのような意義があったと思われますか?
金谷 私は主にテスト自動化の仕組みを構築してきましたが、その結果としてソースコードのリファクタリングが容易になりました。そのうえで、他のメンバーがアプリケーション設計やシステム基盤を整備してくれていることに、大きな意味があると思っています。さまざまなチャレンジを行うための、土台をつくることができました。
芝本 「モノタロウ」の売り上げは右肩上がりで成長しており、将来的に数千億円の売り上げを支えるシステムが必要になります。ですが、システムの良くない部分を放置してしまえば、どこかのタイミングでシステムのボトルネックがビジネスのボトルネックになってしまうはずです。
アーキテクチャ改善とは、ビジネスのチャンスを最大限に活かすために、スピード感のある開発やシステムのスケーラビリティを維持するうえで必要不可欠なものだと思っています。
藤本 たとえビジネスがうまくいっている企業でも、新しいシステムに移行する機会や最新の設計プラクティスを導入する機会に恵まれないケースもあるはずです。ですが、私たちはビジネスの成長とエンジニアリングの挑戦を両立できています。エンジニアにとって、非常に挑みがいのある環境になってきていますね。
取材・執筆:中薗昴/写真:スガワラナオヤ
*1:MonotaRO Tech Blog内の「ペアプロ × テスト駆動開発 で若手をユニットテストに目覚めさせる」にて、実施した施策の詳細が解説されている。「(1)動かないユニットテストを書く」「(2)ユニットテストが動く状態にする」「(3)ユニットテストが動く状態を維持しつつ、リファクタリングする」というテスト駆動開発のお作法に沿って、学習を進めていった。
MonotaRO Tech Blog内の「ペアプロ × テスト駆動開発 で若手をユニットテストに目覚めさせる」にて、実施した施策の詳細が解説されている。「(1)動かないユニットテストを書く」「(2)ユニットテストが動く状態にする」「(3)ユニットテストが動く状態を維持しつつ、リファクタリングする」と↩